- List all the features you would like to develop
- Define an appetite for the task (day, week, month, quarter)
- Identify the features where the appetite differs between individuals and discuss them to reach consensus
- Define the roles necessary to complete the task
- Identify dependencies between features
- Categorize the dependencies
- Soft: somewhat depends on this other feature but isn't blocked by its absence from the codebase
- Hard: depends on this other feature and is blocked by its absence from the codebase
- Prioritize the features
- Use the RICE framework
- Priority can also be skipped if using ROI as prioritization metric
- Estimate the value of a features in dollars
- Calculate a ROI (return on investment) as the estimated value of the feature divided by the defined appetite
- Order tasks according to dependencies and ROI
How can I reduce my error rate when using my skills?
When using skills in which you can make mistakes, it is important to monitor what you do and where you make your mistakes. Like any performance optimization problem, you want to figure out where you make the most mistakes and where you'll benefit the most from fixing those mistakes. If you make the same mistakes 100 times at the cost of a minute each time, that may be preferable to making 1 mistake that cost 100 minutes to fix, given that once the mistake happens, the cost stays the same.
Document your skills. Write down what you do, when you do it (triggers) and what kind errors you make during those steps. Evaluate how many times you make that mistake and how long it costs to fix. Then when you use your skills, track when you make mistakes and how long it takes you to fix the mistake.
As an example, think of a software engineer doing code reviews. Reviewing code requires going through a variety of checks: is the build passing? is the functionality properly implemented? are there tests? are the new files in the proper location? Without a list, the engineer is left looking at the code without any clear checklist. If he is methodical he will have a list he goes through in his head. If not, then he will most likely only look at the code and give it a summary opinion, that is, whether he likes what he sees, or not.
In both cases, given a non-explicit methodology, is it hard to assess where the mistakes are made and which mistakes cost the most to fix. If you don't check that tests were written for the new functionality or changes, what impact will it have in the future? Depending on how likely the code is to change, the likelihood that something gets broken may be significant. While adding a test may require a few minutes, one has to judge how much time would be wasted if a bug were introduced in the piece of code. As code complexity increases, so does the cost of fixing issues in that code.
With an explicit checklist you will be able to track the things that you want to verify before code is merged. As your checklist covers more and more case, this list will reduce the likelihood that the person who wrote code made a mistake that gets into production. By the same token it will increase your effectiveness as a software engineer to produce quality code.
There are no tests on the project I joined. How do I get started?
When joining a new project without tests, here is the value you need to provide through the addition of tests:
- the application works and doesn't crash
- the application works and supports a few input cases
- the application works and supports a variety of input cases
- the application works and is robust to most input cases
Start by finding the main entrypoint of the program and call it in a test. Your test doesn't have to do much, other than ensuring you can start the program and possibly exit. Your goal should not be to assert anything yet, but to exert the code. Create a few tests that do very few things other than starting and terminating the program. Once you've covered a few use cases, you can use those tests to ensure that the application can start, do a few things, then terminate without crashing.
Start unit testing the various parts of the code that are critical. To determine what is critical, you'll have to dig into the code. With the tests you initially wrote you will get a sense of the "critical" pieces, simply due to the fact that they are executed whenever the program starts and stops, which are two things you always want to work.
When writing unit tests, always aim to write a test case that covers the happy path first. You want to demonstrate that the functionality a class or function is supposed to provide is there first and foremost. Then you want to test its robustness and its ability to handle a variety of input cases. Given a large codebase, start by covering most of the code with the happy paths before you start to dig into the special cases.
How can you tell between noisy and useful code refactoring?
The classical adage in software development is that "if it ain't broke, don't fix it". Code may not be in an ideal state, but you should not focus on refactoring it if you aren't working to change it to do something else or so that it can be used in other parts of the code. It is fine to do code refactoring from time to time, especially if you have free cycles and know about a few pieces of code that have been bothersome. However, spending time refactoring systems that are already working but a bit clunky may not be the best use of time, especially if you or your team don't have free cycles to spare to review your changes. A better use of time may for example to help others on their tasks or to prepare future work so it goes smoothly.
In a business environment, a refactor also means implicating other developers to review your changes. As such, this introduces distractions in those developers that could be more focused.
In this case, whether a refactor or other changes to the code is noisy is a matter of timing.
How do you estimate the size of a project roughly?
When I am asked to provide a rough estimate of the required effort on a project with a lot of requirements or user stories, I first want to make sure that my estimate is in the right order of magnitude. That means that I want to estimate a project that takes 1-10 weeks to be in that range, but not estimate less than a week or more than 10 weeks. Similarly, a project that takes one or more years should not estimated as a job of a few weeks.
My orders of magnitude are as follows:
- 1 day
- 1 week (5 days)
- 1 month (20 days)
- 1 quarter (60 days)
- 1 half-year (120/125 days)
- 1 year (250 days)
As such, when estimating a task, I will say that the task will either take 1 day, 1 week or 1 month. In general, any task that is estimated at 1 month long (or above) needs to be broken down into sub-tasks as it indicates that the task is hiding a lot of complexity.
With this kind of approach, one can estimate that a developer can do approximately 250 small tasks (1 day), 50 medium tasks (5 days) or 13 large tasks (20 days) per year.
Start by creating an estimate of the overall project without thinking about any of the underlying tasks. This is done to have a quick idea of the scale of the project.
Then list and estimate the tasks that will need to be accomplished to complete the project. You might be forgetting a few, but it is fine at that moment. Think mostly of the most important tasks and also the riskiest.
If the sum of the efforts you estimated is in the same order of your original project estimate, then you are done. If not, then you need to investigate and explain what led you to either over or under estimate. Did you forget to estimate some tasks? Did you ignore some tasks when you did your initial estimate? What were the assumptions you made that were right/wrong? Once you are satisfied with your explanation, you are done with estimating.