ADR 0002: SM-2 Algorithm for Spaced Repetition
Status
Accepted
Context
We need a scheduling algorithm for flashcard reviews. The app targets users who want Anki-style spaced repetition on a handheld device.
Options considered:
| Algorithm | Pros | Cons |
|---|---|---|
| SM-2 (SuperMemo-2) | Proven, simple, same as Anki | Less sophisticated than newer algorithms |
| FSRS (Free Spaced Repetition Scheduler) | Machine learning, more accurate | Complex, needs training data, harder to implement |
| SM-17 (SuperMemo-17) | Most advanced | Proprietary, extremely complex |
| Custom interval | Simple | No optimization, poor retention |
| Leitner system | Simple, visual | Less efficient than SM-2 |
Decision
Use SM-2.
Consequences
Positive
- Same algorithm as Anki — users already understand it
- Simple to implement (~30 lines of Rust)
- No machine learning dependencies
- Well-studied, proven effective for decades
- Easy to debug and reason about
Negative
- Doesn’t adapt to individual user patterns as well as FSRS
- Fixed EF change formula; newer research suggests improvements
- No optimization for specific card types
Implementation
#![allow(unused)]
fn main() {
pub fn review(&mut self, quality: u8, now: DateTime<Utc>) {
let q = quality.clamp(0, 5);
if q < 3 {
self.repetitions = 0;
self.interval_days = 1;
} else {
self.repetitions += 1;
self.interval_days = match self.repetitions {
1 => 1,
2 => 6,
_ => (self.interval_days as f64 * self.easiness_factor).round() as i64,
};
}
let ef_change = 0.1 - (5 - q) as f64 * (0.08 + (5 - q) as f64 * 0.02);
self.easiness_factor = (self.easiness_factor + ef_change).max(1.3);
self.due_date = now + Duration::days(self.interval_days);
self.last_reviewed = Some(now);
}
}
Future work
Could migrate to FSRS later if user demand exists. SM-2 data is compatible — we’d just recompute intervals with the new algorithm.