editione1.0.1
Updated August 7, 2023When youβre working on a new feature, a bugfix, or almost any project, you may not have all the information you need in order to complete the task. The devil is in the details, and an experienced senior developer knows when they need to ask the project owners to clarify small but important details before proceeding.
Sometimes, the small details wonβt change the implementation much, but other times, a slight clarification to the projectβs requirements could have major ramifications for the design and implementation of your solution.
Here are a few things to consider when you may not have all the information you need to move forward with a project.
Before asking for clarification, first try to break down the requirements into the smallest pieces of individual features you can. Try to think about your task in terms of the following requirements:
Inputs. Determine which inputs are required, which ones are optional, and what the expected values or ranges should be for each input. This helps you dig deeper into what inputs you should expect so that you can build logic around them to prevent bad data from getting into your system.
Try to think in terms of how the system will handle different scenarios. You will naturally come up with some additional questions for your teammates or the product owner if you can think of edge cases where a user may enter some unexpected input.
The name field is only one input field in the design. What if someone only gives us their first name when we need their full name? Should we break it into two fields so we can make each one required?
Are we verifying emails to make sure they are deliverable, or is basic email format validation good enough?
Will the number always be an integer? Or should we expect decimals too?
Will the data streaming from the widgetβs sensor always be in a range between 0.0 and 1.0? What should we do if we receive a negative number from the sensor? And can this value be null if there is an issue with the sensor?
The more clarification around your inputs, the more robust your validation logic and error handling will be. Common errors occur when a program encounters unexpected inputs, so if you can ask good questions to clarify any missing requirements, youβll be able to prevent errors before they happen.
Outputs. Double-check what type or format your outputs should be in, especially if you expect the output to become the input in another program or function. This is easier if youβre using a strongly typed language, and it is critical if youβre using a scripting language.
βexampleβHere are some examples of what to ask when clarifying output requirements:
Should we output the data in CSV, TSV, JSON, or give the user options for all three?
Does the data weβre outputting contain user-submitted content? Does our templating framework escape outputs automatically? How can we avoid XSS attacks?
How many results do you want in the API response? Should we paginate the results?
What should the precision on our floats be when we format them during rendering?
Error handling. Reliable software programs contain robust error handling. Reliable programs gracefully recover from certain errors and display helpful information to the user if the program is unable to proceed. In order to write code like this, you have to anticipate ways in which your program can fail. If you understand all of the things that can go wrong with your code, you can then add logic to handle those errors gracefully or to exit the program and display a helpful error message to your users.
βexampleβHere are more example questions related to error handling:
If the API request fails, how many times should we retry? Should we only retry for specific status codes?
What error message should we display to the user if they hit [insert rare corner case here]?
Should I throw an exception if we hit [insert weird edge case here]?
If youβre unsure of how to proceed when a block of code may fail, ask your coworkers or the project stakeholder. They will often help clarify how you should handle errors in certain situations.
Business logic. The business logic encodes into your program the core set of business rules that determine how it should store, modify, and delete data. Many times, this logic will closely resemble real-life concepts such as inventories, invoices, accounts, or processes.
Making assumptions in business logic can lead to misunderstandings in how the business operates, so itβs always important to clarify any ambiguity before proceeding. A small bug in your business logic can have enormous downstream effects such as data loss, duplicate data, or many other unintended consequences.
βexampleβHere are a few example questions for clarifying business logic.
Should we start the free trial when the user creates their account or after they verify their email?
Should we send an alert as soon as the sensor temperature reaches the high heat threshold, or after itβs above the threshold for more than X seconds?
Should we disable the form after the user submits their answers, or should we allow multiple submissions?
If you donβt have all the information you need to complete a task, youβll need to ask for clarification. Itβs your responsibility to find gaps in the requirements and to ask questions that will clarify any ambiguities and fill in those gaps. The better youβre able to fill in the missing requirements, the better software youβll write, and that starts with communicating clearly to others when you arenβt sure how to handle specific scenarios in your code.
Programmers enjoy autonomy in their role. When youβre starting out in your career, youβll need some help when completing your tasks, and thatβs okay. By the time you start a new task, a lot of the big decisions will already have been made for you, and your job will be to implement a predetermined solution. This is good because it allows you to focus on writing quality code rather than trying to implement a solution where the end result isnβt crystal clear.
When you are implementing a set of requirements someone else has already figured out, you can focus on good coding fundamentals and understanding how your changes fit into the larger context of the codebase. In a well-architected system, sometimes youβll be able to add powerful new functionality by making a few small tweaks, and in doing so, youβll see firsthand how good code should be written. Youβll pick up new ideas over time and form your own opinions based on what youβve seen work well in the past. In the process, youβll build confidence to make more decisions on your own in the future.
As you gain experience and grow into a seasoned developer, youβll naturally want to make more decisions on your own rather than be told how to implement solutions. This is a good thing, but it can be a difficult time in your career to navigate. At some point, youβll find yourself at a crossroads where youβre confident enough in your ability to devise a working solution, but your ideas may not be sufficient for what the senior engineers had in mind.
Youβll disagree on how some solutions should be implemented. These conflicts are difficult because emotions often get the best of people. Sometimes, youβll be right, but other times, the senior engineers will rely on their experience to override your decisions. Sometimes, they will have legitimate reasons based on experience to push back on your decisions, but other times, it may come down to personal preference.
Itβs easier said than done, but as a junior engineer you need to do your best to take your emotions out of the development process. If you can separate your decision-making process from how you view your coding ability, youβll be able to navigate through your career better.
As you gain experience, youβll encounter ambiguity in requirements that might seem insignificant in the context of the task, and itβs natural to want to make decisions on your own. You were hired because youβre smart, and your employer values your ability to make good decisions, so why wouldnβt you want to make certain decisions on your own? After all, Steve Jobs famously said that βit doesnβt make sense to hire smart people and tell them what to do; we hire smart people so they can tell us what to do.β
The longer you work as a software engineer, the more youβll gain a better feeling for what to do when faced with ambiguity, but be careful in these situations. Making your own decisions can be rewarding, but if youβre not careful, you may be making decisions based on flawed assumptions.
An assumption is a prediction that something is true without proof or evidence. The thing that makes assumptions dangerous is that theyβre often made based on experience, and as a junior engineer, you may not have enough experience to safely make certain assumptions. Senior engineers, on the other hand, may have relevant experience to contradict your assumptions, which often leads to conflict.
When a senior engineer pushes back on your solution, you may feel like youβre being personally attacked, but try to view their experience as an opportunity to learn why theyβre pushing back. Most of the time, itβs as simple as asking why they think your assumption is wrong. You may be surprised that they mention some reason you hadnβt considered or some edge case that you didnβt think was possible.
The important thing to understand in these situations is that there are still a lot of things you donβt understand about software development. The same can be said about senior engineers as well, but itβs especially true for those who have only been working professionally for a few years. You may think you know quite a bit, but the reality is that youβve only scratched the surface.
So how do you avoid these conflicts while youβre building up the confidence to make your own decisions? The answer is to make assumptions, but verify those assumptions before implementing any of your decisions. Itβs a subtle detail, but itβs an important one. You can still make decisions on your own when the opportunity arises, but verify with your manager, a senior member on your team, or a project stakeholder before proceeding with any development work.
When you verify your assumptions before beginning any coding, youβll avoid any situations where youβll have to redo your work because you made a decision that wasnβt correct or that someone disagreed with. Itβs faster and cheaper to refactor an idea than it is to refactor code, so itβs always good to double-check with others that your assumptions are correct and that youβre not missing some important context before implementing your decisions. If your assumptions are correct, thatβs great! Youβre getting smarter and your experience helped you come to the correct decision. If not, ask for clarification on why your assumptions were incorrect, or why your decisions wonβt work in the given situation. And by coming to a conclusion before writing any code, youβll be able to avoid conflicts after youβve put in time and effort coding an incorrect solution. Itβs a win-win for everyone involved, and in the worst case, youβll walk away learning something new about why your decision was flawed and what you can do better next time.
At the beginning of this section, we discussed why children ask questions, and how it helps them connect ideas and form a better understanding of the world around them. In a way, most engineers also do this without even thinking about it. By asking the different types of questions youβve learned about in this sectionβasking for help and asking for clarificationβyouβll naturally learn things from the answers youβre given. Youβll learn why you should choose one software design over another, or why you should consider a certain edge case when you didnβt think it mattered.
In some cases though, youβll just have curiosity for how something works or why something is the way it is. You wonβt necessarily be asking for help or to clarify anything, but youβll still have questions. In this case, youβre just asking a question for the sake of learning.
Sometimes itβs good to challenge the status quo and ask your teammates why something is the way that it is. In some cases, the answer may be underwhelming. You may receive an answer such as βwell, thatβs just how weβve always done it.β In asking these questions, youβre challenging the other engineers to think through why theyβve always done it that way, and what they could do to improve a process or part of the codebase.