Think of time as a series of timestamps
I've been logging my daily time usage for almost 5 years. And by "daily time usage", I mean "every minute". Every day, I know where I spend every minute (sleeping, working, reading, writing, coding, etc.). As you may expected, it is not an easy task to do.
Logging daily time usage is not easy
When I first started logging time, I thought of time usage as ranges:
T has a clock, and it starts from a point
t1, ends at a point
t2, lasts for
It's intuitive, but it didn't work well.
- When starting a task, I need to note down what I'm going to do. But it may stop me from doing the real task.
- When adjusting a range, I need to adjust two data points at the same time: change one of
t2, then recalculate
- When switching from task A to task B, I need to stop task A first, then start task B.
- When multitasking, I need to keep multiple clocks running at the same time.
After several rounds of iteration, I now think of time usage as just two timestamps:
T has a start point
t1, and an end point
Here is how I do it now:
- Whenever I need to spend a block of time on something, I jot down a timestamp
- Then when I finish the task, I jot down a timestamp
- Finally I specify which task
TI was working on, and set
t1as its start time,
t2as its end time.
- If I start another task
t2is the start time for
It's a more lightweight and flexible workflow.
Logging code time usage is not easy, either (and how Heroku solves this problem)
More interestingly, if you think of time logging beyond our daily time usage, you'll find many other things that require time logging. One of them is logging time spent in different parts of our program. Heroku faced this exact problem before. In this post, Fred Hebert shared how they solved it.
Their first straightforward solution was like how I started logging my daily time:
- doing the real work
- calculate the duration
But this solution would couple the code for time logging and the code for doing the real work together.
Then a better solution was like what I am doing now:
t1with a label
l1: T started
t2with a label
l2: T stopped
- the analyzing library uses
t2, l2to calculate how long task
- the analyzing library may use
t2for other task calculations like
With this solution, Heroku now fully decouples how the real work gets done (business logic) from how the time logs are analyzed (logging logic).
The power of data and deferring decisions
Optimizing time logging reminds me of Use Return Value to Defer Decisions.
In the case of Heroku, with the help of additional label
l2, we can defer the decision of how to analyze time logs to later.
Again, this is the beauty of functional programming: constraints breed creativity. To quote Fred:
it was the structural constraint of already being in an immutable context that prompted the cleaner design.
This was the first time I learned to think of time as a series of timestamps.
An interesting question after watching this talk: Can we model time with Monoid?