Mon 10 March 2025
Flash Cards
A while back I found out that Jnr. Doctors are keen on using flash cards to help them revise for exams. It occurred to me that I could also use flash cards to drill some of the concepts that would come up in discussions about software systems and system design.
At the time I was developing on a raspberry-pi with a 7-inch screen and I was limited to how much I could install on the little SD card. At first I tried to use a browser based flash card tool, but I was in the mood for doing some deep dives to understanding how these actually work under the hood.
This led me to develop Kanki a bespoke flash card tool for the command line. In this blog post I break down the process I typically use to undertake a software project of this scale (i.e tiny scale).
Requirements
Like all good software projects we start with the high level expectations. What do we want this system to do? (Some of these are quoted straight out of my notebook.
- I want to have 1000+ flashcards based on software and system design
- I should be able to hide the answer, I should be able to reveal the answer.
- Can I input "correct", "close", "wrong" etc and update an SR algorithm to handle recall.
- Nice to have, filtering by topic.
In order to understand how flash cards operate you'd need to dig into figuring how the SR works:
Spaced Repetition: Newly introduced and more difficult flashcards are shown more frequently, while older and less difficult flashcards are shown less frequently in order to exploit the psychological spacing effect. The use of spaced repetition has been proven to increase the rate of learning. - Wikipedia
Entities
We need to know what things we will be working with:
Card |
---|
- Question (string) |
- Answer (string) |
- Topic (string) |
- DeckID (int) |
The card is the question and answer entity which I might have 1000 of (In the end I think I had about 60).
Deck |
---|
- Id (int) |
- Name (string |
The deck is the entity which groups cards, so a card belongs to a deck and this allows me to have cards around separate topics.
Interface
This isn't far off from what I drew in the notebook but clearly I had a vision and the following sort of captures it at the time:
I want to have a view which allows me to load a specified deck. Create a new deck or add cards to an existing deck.
Load: -> deck_a, deck_b
New: (deck)
Add: (cards)
I want to display the different inputs I can provide as answers to each card.
Again, Hard, Good, Easy
< 1min < 6min < 10min 4 days
These are some of the settings I'd like to fine tune as I get used to playing with the flashcard system.
config:
new cards / day
review cards / day
Spaced Repetition Algorithm
This is where things get slightly more involved since I had to understand how Anki and other flash card apps applied repetition. It was mostly finding the one that suited me best and felt the most natural to play with.
This is the algorithm I went for:
step 1: Cards have a queue
field (set to 1 for 'learning') and a
due
field (in minutes) determining when a card should be shown again.
step 2: review cards, already studied but are now due to
be relearnt. queue
field set to (2) and due less than or
equal to today.
step 3: 'new', queue is set to (0), these new cards are capped.
Essentially this determines how we populate the flash cards during a session. Using the three queue types:
- 0:
new
- 1:
learning
- 2:
review
- 3:
relearning
(This is essentially a card deemed as "learnt" but you've forgotten it and now it needs to be relearnt).
Properties of a card.
- due: the timestamp for when the card should be shown again.
- left: how many learning steps before the card graduates to review.
- reps: how many times the card has been shown.
- ivl: tracking the card interval (time to add when recalculating due).
Input
When answering the flash cards there's four inputs you can provide, each of which will cause a different effect, these decide the cards position in one of two or three queues. The result of these inputs also depends on which queue the card is currently in. You can see this here
Again
: Reset number of training repsHard
: +1 rep, show again in 1 minGood
: +1 rep, show again in 5 minEasy
: +1 rep, show again in 1 day
Development
Up until this point I hadn't done a single line of code. The research done above was undertaken before I started writing any code. This helps me have a clear understanding of what I'm actually trying to do. It's far easier to scribble away in my notebook that it is to change code.
There are a few things I needed, which I learnt after I'd written the tool. Things such as being able to edit a card, but these were not major features.
The other thing that I find interesting (or obvious) is solution for the queue. During a learning session you need to have the card queues be dynamic since you're popping the earliest due card and then you're sorting them by when they are next due so the implementation fits using a heap quite well. You can see me initialising the heap here.
Conclusion
I really enjoyed reading up on some of the complexities involved in getting the spaced repetition working in a way that allows new topics to be slowly introduced while working on cementing topics that were in the progress of being learnt. It feels a little exciting when you encounter something new during the learning session. I used kanki to prepare on some topics and every so often add new cards to system design.
One thing I found quite useful is getting someone else to ask you the questions verbally and they make a judgement on how close you are to the answer. This can get you more comfortable with talking about these topics and the person reading the questions can help keep you honest about how well you know a topic.
Lastly it's also interesting that learning is effectively broken down into three separate speeds. 1. This is new. 2. I'm actively learning this. 3. I should know this.