In the name of God, stop a moment, cease your work, look around you. Leo Tolstoy
A program is made of a number of subsystems. Each of those subsystems is composed of a smaller parts - components, modules, classes, functions, data types, and the like. Sometimes even boxes and lines. Or clever ideas.
The jobbing programmer moves from one assignment to the next; from one task to another. Their working day is composed of a series of construction and maintenance tasks on a series of these software components: composing new parts, stitching parts together, extending, enhancing or mending existing pieces of code.
So our job is simply a string of lots of smaller jobs. It's recursive. Programmers love that that kind of thing.
Are we there yet?
So there you are: getting the job done. (You think.)
Just like a small child travelling in the back of a car constantly brays “are we there yet?”, pretty soon you'll encounter the braying manager: “are you done yet?”
This is an important question. Its essential for a software developer to be able to answer that one simple request: to know what “done” looks like, and to have a realistic idea of how close you are to being “done”. And then to communicate it.
Many programmers fall short here; it's tempting to just keep hacking away until the task seems complete. They don't have a good grasp on whether they're nearly finished or not. They think: There could be any number of bugs to iron out, or unforeseen problems to trip me up. I can't possibly tell if I'm almost done.
But that's simply not good enough. Usually, avoiding the question is an excuse for lazy practice, a justification for “coding from the hip”, without forethought and planning. It's not methodical.
It's also likely to create problems for you. I often see people working far too hard:
They are doing more work than necessary, because they didn't know when to stop.
Without knowing when they'll be done, they don't actually complete the tasks they think are finished. This leads to having to pick things back up later on, to work out what's missing and how to stitch it in. Code construction is far slower and harder this way.
The wrong bits of code get polished, as the correct goal was never in sight. This is wasted work.
Developers working too hard are forced to put in extra hours. You'll not get enough sleep!
Let's see how to avoid this and to answer “are we there yet” effectively.
Developing backwards: decomposition
Different programming shops manage their day-to-day development efforts differently. Often this depends on the size and structure of the software team.
Some place a single developer in charge of a large swathe of functionality, give them a delivery date, and ask them for occasional progress reports. Others follow “agile” processes, and manage a backlog of more granular tasks (perhaps phrasing them as stories), divvying those out to programmers as they are able to move into a new task.
The first step towards defining “done” is to know exactly what you're working on. If it's a fiendishly large and complex problem, then it's going to be fiendishly complex to say when you'll be done.
It's a far simpler exercise to answer how far through you are through a small, well-understood problem. Obvious, really.
So if you have been allotted a monster task, before you begin chipping away at it, break it down into smaller, understandable parts. Too many people rush headlong into code or design without taking a step back to consider how they will work through it.
Split large tasks up into a series of smaller, well-understood tasks. You will be able to judge progress through these more accurately.
Often this isn't a complex a task, at least for a top-level decomposition. (You may have to drill down a few times. Do so. But take note: this is an indication that you've been handed a task at far too high a granularity.)
Sometimes such a decomposition is hard to do, and is a significant task itself. Don't let that put you off. If you don't do it up-front for estimation purposes, you'll only end up doing it later on in less focussed ways as you battle to the finish line.
Make sure that at any point in time, you know the smallest unit you're working on; rather than just the big target for your project.
You've got an idea of the big picture; you know what you're ultimately trying to build. And you know the particular sub-task you're working on at the moment.
Now, make sure that for whatever task you are working on, you know when to stop.
To do this, you have to define what “done” is. You have to know what “success” means. What the “complete” software will look like.
Make sure you define “done”.
This is important. If you haven't determined when to stop, you'll keep working far past when you needed to. You'll be working harder and longer than you needed to. Or, you won't work hard enough – you'll not get everything done. (Not getting everything done sounds easier, doesn't it? But it's not... the half-done work will come back to bite you, and will make more work for you later down the line, whether that's bugs, rework, or an unstable product).
Don't start a piece of coding work until you know what success is. If you don't yet know, make your first task determining what “done” is. Only then, get going. With the certainty of knowing where you're headed, you'll be able to work in a focused, directed manner. You'll be able to make informed choices, and to discount unnecessary things that might side-track or delay you.
If you can't tell when it's done, then you shouldn't start it.
So how does this look in practice? How do you define “done”? Your “done” criteria needs to be:
It must be unambiguous and specific. A list of all the features to be implemented, the APIs added or extended, or the specific faults to be fixed.
If, as you get into the task, you discover things that might affect the completion criteria (e.g. you discover more bugs that need fixing, or uncover unforeseen problems) then you must make sure that you reflect this in your “done” criteria.
This criteria is usually directly traceable to some software requirements or a user story – if you have them. If this is the case, make sure that this connection is documented.
Make sure that the success criteria is seen by all important parties. This probably includes: your manager, your customers, the downstream teams using your code, or the testers who will validate your work.
Make sure everyone knows and agrees on this criteria. And make sure they'll have a way of telling – and agreeing – when you are “done”.
The nature of each task will clearly define what “done” means. However you should consider:
How much code must be completed. (Do you measure this in units of functionality, APIs implemented, user stories completed?)
How much is design done, and how it's captured.
Whether any documents or reports must be generated.
When it's a coding task, you can mostly clearly demonstrate “being done” by creating an unambiguous test set. Write tests that will show when you've fashioned the full suite of code required.
Use tests written in code to define when your code is complete and working.
There are some other questions that you may have to consider when you describe what “done” is:
Where is the code delivered to? (e.g. to version control)
Where is the code deployed to? (Is it “done” when it's live on a server - or do you deliver testable product ready for a deployment team to roll out?)
What are the economics of “done”? The exact numbers required that may lead to certain tradeoffs or measurements. For example: how well should your solution scale? It's not good enough if your software only manages 10 simultaneous users if 10,000 are required. The more precise your done criteria the better you understand these economics.
How will you signal that you're done? When you think you're done how will you let the customer/manager/QA department know? This probably looks different for each person. How will you garner agreement that you are indeed done – who signs-off on your work? Do you just check in, do you change a project reporting ticket, or do you raise an invoice?
Just do it
When you've defined “done”, you can work with focus. Work up to the “done” point. Don't do more than necessary.
Stop when your code is good enough – not necessarily perfect (there may be a real difference between the two states). If the code gets used or worked on an awful lot, it may eventually be refactored to be perfect – but don't polish it yet. This may just be wasted effort. (Beware: this is not an excuse to write bad code, just a warning against unnecessary over-polishing).
Don't do more work than necessarily. Work until you're “done”. Then stop.
Having a single, specific goal in mind helps you to focus on a single task. Without this focus it's easy to hack at code randomly trying to achieve a number of things and not managing any of them successfully.
Do you know when you're current task will be “done”? What does “done” look like?
Have you decomposed your current task into a single goal, or a series of simple goals?
Do you decompose your work into achievable, measurable units?