tl;dr

Time-tracking tools suck. parti-time just sucks less. Track your time in your favorite editor. Avoid mistakes, gaps and double-bookings by using the time partitioning concept.

Time-Tracking: A necessary evil

We are in the same boat as other IT consultancies. Quite often our assignments have a contract model of Time & Materials or “Arbeitnehmerüberlassung”. In both cases it is vital to maintain a timesheet of the time spent on a project.

Customers expect to receive a report of the accumulated work time and sign it before invoicing.

Prominent Alternative solutions

This challenge is so common-place that plenty of offerings already exist. The problem: They all suck.

The typical pitfall is the user experience. Entering times is an everyday activity. It has to be convenient and fast. It has to support the user in spotting and correcting mistakes. Most solutions sell because of their reporting and management features and treat time entry as a poor cousin. This is no wonder, as the solutions are sold to managers. Guess what managers value: good reporting and management features.

Another issue is the tight coupling and the vendor lock-in that are easily introduced into your corporate infrastructure with “integrated” tools.

These reasons are roughly the same for most of these solutions. I will not repeat myself when discussing them.

Dedicated time keeping tools (SaaS and standalone)

Of course there are plenty of cloud services and boxed products about time- and expense-tracking. Just to name a few:

In fact, there are so many that just looking through them superficially takes more time than creating your own solution. On first sight none of them seems to offer the key features that I am looking for. I am happy if someone can enlighten me.

Integrated ERP features

Other companies make their employees track their time in an ERP system. This might be fine if you are lucky enough to have an ERP that actually fits the domain of your business model well and – again – provides a good user experience and integration interfaces. We could not find anything like that on the market, yet. Most ERP solutions tend to be too complex and heavyweight.

We especially do not know about any simple and lightweight ERP for small and medium consultancies. The closest thing we know is weclapp. We actually use it for our internal standard business processes like quotation and invoicing. However, we use it as use-case specific generic tool, not as a full-fledged ERP system. Time tracking especially is not one of weclapp’s strengths. Entering time records is just cumbersome.

Project Management Tool Plugins

The ubiquitous tool for project management in the IT industry is Atlassian Jira. Tracking time spent on specific tickets is such a popular use case, that there is a special category for time tracking plugins in the Atlassian Jira marketplace with 188 products matching the filter (as of 2019-11-27).

The most prominent plugins we have seen in the field are:

All of them suffer from the same known issues.

Another flaw seen quite often in Jira is the focus on “booked times” - read: “booked durations”. Keeping consistency of your tracked times quickly becomes a pain in the ass. Some solutions have innovative approaches to fix that issue, e.g. by reports in calendar-views. However, in order to use it you still have to enter your times in point-and-click forms, switch dashboard and so on. All that effort for just keeping a few time records!

There are also plugins to integrate separate time tracking solutions, e.g.

Spreadsheets

So far, we looked at complex and integrated solutions. A common low-tech approach are Spreadsheets like Microsoft Excel, Libreoffice Calc or Google Sheets. They are simple and they have power-user features like good keyboard shortcuts.

Nevertheless, they also have drawbacks:

  • the overhead of the feature-stuffed spreadsheet applications,
  • proprietary storage formats (yeah, I know, there are standards and such, but…),
  • a weak domain model and
  • unclean extension interfaces.

Spreadsheet solutions tend to look and feel great when piloting, but become awkward to maintain with growing feature set and process depth. (In fact we started out our experiments with Google Sheets.)

Still, it seems like we are getting closer.

the first real alternative: ti

Imagine there is a time-tracking solution, that real nerds want to use. How would it look like? One approach to this problem has been created by my colleague Bogdan Maxim in his command-line time-tracking solution ti.

ti is based on the fact that any productive consultant with a technical background will have a console on her fingertips at all times. CLI interfaces are a natural choice to accomodate this with low latency. ti enables you to interact with your timesheet no matter what else you are doing in that terminal, right now. All commands are streamlined to ease the most common use-case: tracking the current day.

ti is rounded up by the essential reporting capabilities you need. A real gem is the calendar display rendered into the console.

I especially want to stress the suite of cram tests that is serving as living documentation. A nice feat!

Similar tools like PlainTimer exist, but I have seen none that is so streamlined.

ti in action

So, how does ti look and feel? It’s all driven by the console. Entering time records becomes a series of commands.

$ ti start 'Customer X 2019-08' 05:45
Started working on Customer X 2019-08 at 05:45.
$ ti note 'Roadmap planning'
Yep, noted to Customer X 2019-08.
$ ti stop 07:00
So you stopped working on Customer X 2019-08 at 07:00.
$ ti start 'Private' 07:00
Started working on Private at 07:00.
$ ti note 'Reading Wonderful Clojure'
Yep, noted to Private.
$ ti stop 07:45
So you stopped working on Private at 07:45.
$ ti start 'Customer X 2019-08' 07:45
Started working on Customer X 2019-08 at 07:45.
$ ti note 'Legacy Stack Analysis'
Yep, noted to Customer X 2019-08.
$ ti note 'Visualisation of Dependencies'
Yep, noted to Customer X 2019-08.
$ ti stop 11:30
So you stopped working on Customer X 2019-08 at 11:30.

Editing the time records as YAML is also possible:

$ ti edit

Your ${EDITOR} is going to open:

work:                                                    
                                                         
- end: '2019-11-29T06:00:00.000001Z'                     
  name: Customer X 2019-08                               
  notes:                                                 
  - Roadmap planning                                     
  start: '2019-11-29T04:45:00.000001Z'                   
                                                         
- end: '2019-11-29T06:45:00.000001Z'                     
  name: Private                                          
  notes:                                                 
  - Reading Wonderful Clojure                            
  start: '2019-11-29T06:00:00.000001Z'                   
                                                         
- end: '2019-11-29T10:30:00.000001Z'                     
  name: Customer X 2019-08                               
  notes:                                                 
  - Legacy Stack Analysis                                
-UU-:----F1  ti.JvM0Yk.yml   Top (2,0)      (YAML) ------
                                         

And whenever you need reports, they are at your fingertips (examples based on the parti-time integration test sample data):

$ ti csv
2019-08-12 | 07:45 | 09:00 |  | Some Task ;  | Customer X 2019-08
2019-08-12 | 09:00 | 09:45 |  | Proof-Reading Metamorphant Blog ;  | Metamorphant
2019-08-12 | 09:45 | 13:30 |  | Development of Blarz ; Interesting other stuff ;  | Customer X 2019-08
2019-08-12 | 13:30 | 14:00 |  | Lunch Break ;  | Private
2019-08-12 | 14:00 | 17:45 |  | Architecture Whiteboard Session ; Incident Blubb ;  | Customer X 2019-08
2019-08-12 | 17:45 | 18:15 |  | Reading Awesome Clojure ;  | Private
2019-08-12 | 18:15 | 19:30 |  | Decision draft Project Y ;  | Customer X 2019-08
2019-08-13 | 07:45 | 09:00 |  | Roadmap planning ;  | Customer X 2019-08
2019-08-13 | 09:00 | 09:45 |  | Reading Wonderful Clojure ;  | Private
2019-08-13 | 09:45 | 13:30 |  | Legacy Stack Analysis ; Visualisation of Dependencies ;  | Customer X 2019-08
2019-08-13 | 13:30 | 14:00 |  | Lunch Break ;  | Private
2019-08-13 | 14:00 | 18:15 |  | Monitoring stack ; Log shipping Integration ;  | Customer X 2019-08
2019-08-13 | 18:15 | 19:00 |  | Phone call with customer Z ;  | Metamorphant
2019-08-13 | 19:00 | 20:00 |  | Automated DEV host setup ; Build pipelines ;  | Customer Z 2019-08
$ ti report 'Customer X 2019-08'
Displaying all entries for Customer X 2019-08 grouped by day:

weekday  |  date  |  total duration  |  start time  |  end time  |  break  |  description  | 
Mon | 2019-08-12 | 07:45 | 19:30 | 01:45 | 10:00 | Some Task ; Development of Blarz ; Interesting other stuff ; Architecture Whiteboard Session ; Incident Blubb ; Decision draft Project Y ; 
Tue | 2019-08-13 | 07:45 | 18:15 | 01:15 | 09:15 | Roadmap planning ; Legacy Stack Analysis ; Visualisation of Dependencies ; Monitoring stack ; Log shipping Integration ; 

Based on your current entries, you should have logged 16:00 ; you instead logged 19:15
$ ti calview 08
Displaying all entries for 2019-08 grouped by day:
+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
|         Monday           |         Tuesday          |         Wednesday        |         Thursday         |           Friday         |
+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
|                          |                          |                          | 2019-08-01               | 2019-08-02               |
+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
|                          |                          |                          |                          |                          |
|                          |                          |                          |                          |                          |
|                          |                          |                          |                          |                          |
|                          |                          |                          |                          |                          |
|                          |                          |                          |                          |                          |
+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
| 2019-08-05               | 2019-08-06               | 2019-08-07               | 2019-08-08               | 2019-08-09               |
+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
|                          |                          |                          |                          |                          |
|                          |                          |                          |                          |                          |
|                          |                          |                          |                          |                          |
|                          |                          |                          |                          |                          |
|                          |                          |                          |                          |                          |
+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
| 2019-08-12               | 2019-08-13               | 2019-08-14               | 2019-08-15               | 2019-08-16               |
+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
|          Private : 01:00 |Customer Z 2019-08 : 01:00 |                          |                          |                          |
|     Metamorphant : 00:45 |     Metamorphant : 00:45 |                          |                          |                          |
|Customer X 2019-08 : 10:00 |          Private : 01:15 |                          |                          |                          |
|                          |Customer X 2019-08 : 09:15 |                          |                          |                          |
|                          |                          |                          |                          |                          |
+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
| 2019-08-19               | 2019-08-20               | 2019-08-21               | 2019-08-22               | 2019-08-23               |
+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
|                          |                          |                          |                          |                          |
|                          |                          |                          |                          |                          |
|                          |                          |                          |                          |                          |
|                          |                          |                          |                          |                          |
|                          |                          |                          |                          |                          |
+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
| 2019-08-26               | 2019-08-27               | 2019-08-28               | 2019-08-29               | 2019-08-30               |
+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+
|                          |                          |                          |                          |                          |
|                          |                          |                          |                          |                          |
|                          |                          |                          |                          |                          |
|                          |                          |                          |                          |                          |
|                          |                          |                          |                          |                          |
+--------------------------+--------------------------+--------------------------+--------------------------+--------------------------+

ti is pretty awesome! But it is still not exactly what I am looking for. I am a big friend of using my screen real estate. I prefer switching tool contexts all-or-nothing. Hence, I keep everything in full screen and leverage it. That is: When entering time bookings by commands I want to see the complete context of already entered times. ti puts the burden of keeping context on me as a user. Booking in and out also does not feel natural for me. At a customer site my typical day is dominated by external interruptions. I will certainly not be able to remember clocking in and out any time I am interrupted. I need to re-assemble the pieces after the fact as conveniently as possible.

The parti-time concept

parti-time follows a different, but equally nerdish, approach.

Inspired by emacs org-mode: It’s all text

One source of inspiration is the community of productivy junkies around GNU Emacs and its phantastic org-mode. For a long time I have been maintaining my journal as org file. Until creating parti-time, I was also doing my shadow time-tracking with org-mode. The key benefits are:

  • Leverage your editor, just as for anything else (you are a power-user, aren’t you?)
  • Leverage version control, e.g. with git
  • Leverage all the awesome existing tooling for manipulation of plain text files, e.g. on the unix command line

However, org-mode files have a fairly feature-loaded and complex format. It is not simple to parse and reproduce. Also, it has too much clutter when our only goal is simple time-tracking.

Inspired by unix: plain text is durable

Despite all attempts to establish more powerful standard formats for long-term solutions, only plain text has survived the test of time. In its principles, it is stable since the 70s. Even minor format conversions like the adoption of Unicode were quite trouble-free.

All other complex higher-level technologies like databases, XML, … sooner or later failed, once the companies supporting the – necessarily complex – infrastructure of libraries and tooling around it lose interest.

When you want to build something future safe and low maintenance, keep it small and simple: build it in plain text. In doubt, you will be able to wrap your own tooling, in case your friendly vendor gives up maintenance.

No wonder, people are using plain text together with git for all kinds of use cases. Examples comprise git-powered wikis like github wikis, ikiwiki or git-wiki, git-powered issue tracking like ticgit-ng, git-issue, git-bug, gitissius or bug and many more.

Inspired by ledger: Special-purpose DSLs

Another source of inspiration is the ledger family (see hledger for a clone in Haskell and beancount for a clone in Python) of accounting tools, commonly subsumed under the term plain text accounting.

I have been using plain text accounting with ledger for private purposes as well as for establishing a kitty cash for micropayments between members of one of my former teams, e.g. when ordering pizza and splitting the cost. The solution is still in use 10 years after I introduced it.

Indeed, some people use ledger for time keeping and invoicing (see the official ledger documentation about time keeping or anarcat’s ledger-timetracking samples). In that case, they follow an accounting analogy with time becoming a currency. Invoicing becomes currency conversion from time to money. Appealing, isn’t it? Anyways, I think the specific ledger language of accounting does not fit my purpose.

However, using a domain specific language (DSL) to describe the essence behind the data reduces clutter and makes for less accidental complexity. Because it is a proper language, it can be used by machines and humans alike. As an additional benefit, it forces the language designer to think thoroughly and capture the real core behind the problem they are solving. During the creation of the parti-time DSL my understanding of the domain sharpened over a number of iteration steps:

  • Starting with a data DSL embedded into YAML
  • Refining that
  • Migrating to an external DSL
  • Refining that in multiple iterations
  • Removing cruft
  • Streamlining last details

Inspired by RMS himself: Freedom to the users

Richard Matthew Stallman a.k.a. RMS is the most prominent proponent of free software, maintainer of the GNU project and until recently president of the Free Software Foundation. I highly recommend learning more about his ideas. While at times they sound radical, they are based on real-world experience and thorough observation and thinking. It is about the ethics of computing.

In a nutshell, we should empower our users and not limit them in their way to use computing. The solutions we provide should enable the user - technically as well as legally - to do whatever they please.

That includes, but is not limited to:

  • Do not make the user dependent on some proprietary, central data → e.g. instead decentralize storage
  • Enable the user to take full data ownership → e.g. use open formats that are simple to parse
  • Give the user full control over the code → e.g. publish the source code under a free license
  • Provide your solutions in ways that are simple to adopt → e.g. make your code easy to understand and extend, provide solid documentation

It is hard to do so in everyday business, but in general I share these moral principles and try to follow them.

Inspired by the Google Maps timeline: No gaps, no double-bookings

Google Maps Timeline is a feature that enables people to track their geoposition over time. The important idea that went into parti-time is that time is a continuum and not a set of from-to time-spans. In each moment you are doing something. Each moment can be attributed to some project or occupation.

Whenever you start a new activity you are splitting a time-slice. Everything after that split belongs to the new activity; until a new split is inserted.

IT people know the same principle from partitioning their hard disk. The hard disk is not exactly a continuum of space, but at least a series of contiguous blocks. In the beginning, all space is free. While partitions are in principle from-to spans, most partitioning tools provide an experience that is more like the splitting analogy. The starting point of the next partition will be implicitly assumed to be the “last allocated block”.

parti-time in action

Now that you learned about the concepts you certainly want to see it in action, don’t you?

Basic Editing in Emacs

I do not want to start a flamewar here, but I have been using GNU Emacs since grammar school. It is a habit that you do not unlearn easily (even if you wanted to).

parti-time includes a simplistic emacs mode to enable syntax highlighting (a.k.a. font locking). Feel free to create and contribute one for your editor of choice. The DSL is super-simple. You will have an easy and pleasant ride.

     Architecture Whiteboard Session                                       
     Incident Blubb                                                        
1545 Private                                                               
     Reading Awesome Clojure                                               
1615 Customer X 2019-08                                                    
     Decision draft Project Y                                              
1730 Private                                                               
                                                                           
2019-08-13                                                                 
0545 Customer X 2019-08                                                    
     Roadmap planning                                                      
0700 Private                                                               
     Reading Wonderful Clojure                                             
0745 Customer X 2019-08                                                    
     Legacy Stack Analysis                                                 
     Visualisation of Dependencies                                         
1130 Private                                                               
     Lunch Break                                                           
1200 Customer X 2019-08                                                    
     Monitoring stack                                                      
     Log shipping Integration                                              
1615 Metamorphant                                                          
     Phone call with customer Z                                            
1700 Customer Z 2019-08                                                    
     Automated DEV host setup                                              
     Build pipelines                                                       
1800 Private                                                               
                                                                           
-UU-:----F1  TimeTracker.sample.tl   Bot (39,0)    Git-master  (Tl yas) ---
                                                                          

What should I say? You just edit it as usual. No need to learn any new commands.

Look into the modeline of that “screenshot”. Both tl-mode and yas-minor-mode have been activated. Use in .emacs

(add-to-list 'load-path "~/.emacs.d/tl-mode/")
(require 'tl-mode)

(defun enable-yasnippet ()
  ""
  (interactive)
  (yas-minor-mode)
  (yas-reload-all))

(add-hook 'tl-mode-hook 'enable-yasnippet)

with a simple .emacs.d/tl-mode/tl-mode.el based on generic-mode:

(require 'generic-x)
    
(define-generic-mode 'tl-mode
  '("#")                           ;; comments start with '#'
  '()                              ;; no special keywords
  '(("^[[:digit:]]\\{4\\}-[[:digit:]]\\{2\\}-[[:digit:]]\\{2\\}" . 'font-lock-function-name-face)     ;; reference dates
    ("^[[:digit:]]\\{4\\}" . 'font-lock-variable-name-face)                                           ;; military times at begin of a timeslice
    ("^[[:digit:]]\\{4\\} \\(.*\\)$" 1 'font-lock-string-face))                                       ;; Category of timeslice
  '("\\.tl$")                      ;; files for which to activate this mode 
  nil                              ;; other functions to call
  "A mode for tl files"            ;; doc string for this mode
  )

(provide 'tl-mode)

That is all you need to be up and running. See the EmacsWiki page about Generic Mode and the GNU Emacs Lisp Reference Manual about Generic Mode for more information how it works.

Snippets with emacs yasnippet-mode

For the yasnippet use, refer to .emacs.d/snippets/tl-mode/day for a sample:

# -*- mode: snippet -*-
# contributor: Johannes F. Knauf <johannes.f.knauf@metamorphant.de>
# name: business day in tl
# key: day
# expand-env: ((yas-indent-line 'fixed) (yas-wrap-around-region 'nil))
# --
`(format-time-string "%Y-%m-%d")`
0545 Customer X `(format-time-string "%Y-%m")`
0700 Private
     Reading
0745 Customer X `(format-time-string "%Y-%m")`
1130 Private
     Lunch Break
1200 Customer X `(format-time-string "%Y-%m")`
1515 Private
     Reading
1600 Customer X `(format-time-string "%Y-%m")`
1700 Private
$0

Enter day into your timeline file.

day 

Press <TAB> (default key binding for yas-minor-mode).

2019-12-02                                                                 
0545 Customer X 2019-12                                                    
0700 Private                                                               
     Reading                                                               
0745 Customer X 2019-12                                                    
1130 Private                                                               
     Lunch Break                                                           
1200 Customer X 2019-12                                                    
1515 Private                                                               
     Reading                                                               
1600 Customer X 2019-12                                                    
1700 Private                                                               
                                                                           

Your snippet will be expanded.

Getting reports

The set of reports is focused on the everyday use of parti-time’s users, i.e. foremost me and only me. From the official parti-time README:

Create reports for invoicing customers:

$ lein run invoice-report src/itest/resources/examples/v2_tl/TimeTracker.sample.tl "Customer X 2019-08"
2019-08-12,05:45,17:30,01:45,10:00,"Some Task, Development of Blarz, Interesting other stuff, Architecture Whiteboard Session, Incident Blubb, Decision draft Project Y"
2019-08-13,05:45,16:15,01:15,09:15,"Roadmap planning, Legacy Stack Analysis, Visualisation of Dependencies, Monitoring stack, Log shipping Integration"

Create a project summary for a complete timeline file (I currently keep my timeline in 1 file per month):

$ lein run projects src/itest/resources/examples/v2_tl/TimeTracker.sample.tl
["Customer X 2019-08" 19.25]
["Metamorphant" 1.5]
["Private" 14.5]
["Customer Z 2019-08" 1.0]

Create a timesheet for import into record-based time-tracking systems:

$ lein run timesheet src/itest/resources/examples/v2_tl/TimeTracker.sample.tl
2019-08-12,05:45,07:00,,Some Task,Customer X 2019-08
2019-08-12,07:00,07:45,,Proof-Reading Metamorphant Blog,Metamorphant
2019-08-12,07:45,11:30,,"Development of Blarz, Interesting other stuff",Customer X 2019-08
2019-08-12,12:00,15:45,,"Architecture Whiteboard Session, Incident Blubb",Customer X 2019-08
2019-08-12,16:15,17:30,,Decision draft Project Y,Customer X 2019-08
2019-08-13,05:45,07:00,,Roadmap planning,Customer X 2019-08
2019-08-13,07:45,11:30,,"Legacy Stack Analysis, Visualisation of Dependencies",Customer X 2019-08
2019-08-13,12:00,16:15,,"Monitoring stack, Log shipping Integration",Customer X 2019-08
2019-08-13,16:15,17:00,,Phone call with customer Z,Metamorphant
2019-08-13,17:00,18:00,,"Automated DEV host setup, Build pipelines",Customer Z 2019-08

The reports are definitely an area that still needs some polishing, but it actually shows where the focus and priority of parti-time is: With the everyday user.

UX for power users: Call for a manifest

With that simple example, I would like to call for a whole new discipline: UX for power users.

That is: UX beyond web and mobile. UX that supports daily users in stupid routine tasks as well as possible.

It does not have to look fancy. It has to work. Imagine a good old Cobol text UI with good key bindings. Or text UIs with limit-view capabilities like aptitude, mutt and others.

It is the right time. People are just rediscovering old virtues that we nerds have never abolished in the first place. Apple is introducing a dark mode. What an innovation! We have never left dark mode since the 80s.

Stay tuned!



Post header background image by wal_172619 from Pixabay.


Contact us