The digital version of a sticker, large letters that exclaim 'Definition of Done Done Done' with our logo below.

The definition of Done Done Done

Is it really done once it's merged? Or done done, once it's rolled out on staging? At crafted., our done is when it's in use in production. Only when it's "done done done" like that, the next thing may come up.

Example for a team’s definition of done

  • Passes all tests
  • Meaningful commits using Conventional Commits
  • API Version bumped according to semantic versioning if necessary
  • Feature-flagged with expiry date in 4 weeks
  • Successful rollout in production

A definition of done can be a useful agreement for teams. It may spell out the non-functional requirements such as “passes all tests” or “includes only backwards-compatible database changes”. It’s a team-level agreement on how the team wants to develop software, a mutual reassurance of what is necessary and permitted to be done in order to do a good job.

However, for us at crafted., the ultimate measure of “done” is when it’s running in production and is used by users. Only then may we move on to the next feature.

The reason we set the bar so high?: To limit Work in Progress.

Too often, teams move on to the next thing too early, before work is finished. The more often this happens, the more work you’re juggling at the same time – your Work in Progress increases, and yet nothing gets done done done.

Software continuously disintegrates…

Having too much Work In Progress accelerates the deterioration of a system in many ways:

  • Context Switching: Most obviously, picking up half-finished work from two weeks ago costs time, just to get back into the task and to understand where things have been left at. Whatever “oh I have to make sure to remember that”-items you were juggling in your head back then might not reoccur to you this time. And not just that, context switching gets more expensive and harder with the amount of work in progress and how close to done it is – obviously, as there’s more context to switch to.
  • Cutting corners: The more you have to do, the more likely you’ll cut a corner, just to check one item of your list. You might end up doing the bare minimum, at best the happy path, but not consider edge-cases. (This is symptomatic for a lot of bugs we experience as users once we venture off the happy path just by a little.)
  • Sacrificing non-functional requirements: Ironically, as a consequence of picking up new work before being “done done done” with your current work, the team-level definition of done is quickly undermined: The commit messages get sloppier, or we’ll skip some tests that would take a long time to write,
  • Skipping chores: With too much Work in Progress, there’s little chance that chores that maintain the codebase will make it to the top of your list. The big refactor to improve error handling, significantly improving test execution speed, upgrading major dependencies – not doing these will come and haunt the team in due time.

”Ready for testing” isn’t half-way to being done

It’s so tempting to get stuff “done”. But “ready for testing”, “deployed to staging” or “planned for release” are just deceiving you. At best, they indicate some progress on the way to “done done done”, but picking up another work item now will prove fatal soon:

  • Ready for testing: If someone testing your change is on your critical path to production, it either is so crucial that any feedback from testing will necessarily result in rework, or it’s a step that shouldn’t stand between you and “Done Done Done”.
  • Deployed to staging: For a lot of teams, this is the first time their system may be tested end-to-end. Whatever API documentation your partners provided, this will be the first time their systems will reveal whether it’s worth the CPU cycles spent generating it. Expect surprises here, and resist the temptation to pick up something new just yet!
  • Planned for release: The worst time to pick up something new, and yet the one that feels closest to “done done done”. The testers have tested to their heart’s content, the third-party integration works, and you’re confident that users will love the new feature. Until the feature is released, it is effectively stale, even worse, it may start to disintegrate as there’s no one watching it – no developer working towards releasing it, and no user actively using it. In Lean terms, we’ve produced inventory that’s going stale until it’s finally being put to use.

The fear of being unproductive

Things to do instead of picking up the next thing

  • Start a major update of a dependency before we lag behind too much
  • Pick the slowest test, make it faster
  • Pick the most unreliable test, fix it or delete it
  • Add a regression test for the last you fixed
  • Find code to delete:
    • Commented out code
    • Noisy log lines
    • Dead classes

A filled backlog, a project overdue, a bug task waiting to be expedited – whether one is anxious about actually feeling unproductive, or “just” to be perceived as such, our work environment regularly rewards high throughput over being thorough and long-term sustainability.

At crafted., we’d much rather give more attention to an equally valid list of tasks that one can do instead of picking up the next work item, with the understanding that working on them will make the next work item better and faster. And the one after that. They don’t need to be negotiated beyond the team, as they are part of regular maintenance and the foundation of being able to do a good job going forward.

Some of these tasks we regularly encounter that always have a positive return include for example:

  • Spending half a day to make the delivery pipeline 10 minutes faster: This will save hours of busy-wait time and context switching for everyone over the course of a year.
  • Updating to the new version of a core dependency while you’re only one major version behind: In three years, you will lag behind 3 major versions. The result are PHP8 and Java17 migration projects, stalling feature development for months if not years.

At crafted., we’ll make sure these investments in long-term sustainability and maintainability are recognized and valued, so that they become viable alternatives for that time when we run out of work and would almost pick up the next thing.

Underutilization enables sustainability

The saying goes “A highway with 100% utilization is a traffic jam”. If the on-ramp is filled with cars too, it’s a recipe for disaster. We need that extra slack for the unforeseen – even though it might be foreseaable, like testers having feedback, or integrations failing once they’re being used.

A crafted. team will manage it’s Work In Progress at both ends: By releasing often and releasing early; by accompaning work until it’s “done done done”; and by learning and improving just when we can take on new work, without compromising the quality of what we’re already working on.

How? By automating, by testing, by monitoring, and most importantly, by doing what needs to be done to build software sustainably.

Until the work is “Done Done Done”, this time for real.