A critical skill
In this article, I would like to talk about a critical skill in any project and any programming language. A skill that is mastered by experience and it's hard to teach. A skill that improves your development speed and quality: Approaching Development Tasks. From the moment you read the task until you make the pull request.
TL;DR: How should you approach development tasks? Don't Bite More Than You Can Chew
We'll use two full-stack examples throughout the article:
- Task 1: Place an avatar of the user in the navigation bar and show a dropdown on hover.
- Task 2: The user has a new attribute, phone number.
Before development: Ask
Before we start any coding, we need to make sure that we understand and have all the information for the task.
Never be afraid to ask questions if something is not clear. It's better to discuss a topic instead of making a false assumption and losing time.
- Task 1: Do all users have an image avatar? What if not?
- Task 2: Can the user add the phone number during registration or only on the edit profile page?
Questions can be of different types, from requirements to edge cases and technical questions.
For example, a common source of discussion is the implementation design. If a similar feature has already been implemented, we can copy the same pattern to solve the new task. Yet, if this is the first time a solution like that is implemented, it makes sense to spend some time discussing the architecture with a team member.
- Task 1: Should we reuse a dropdown that already exists or create a new one?
- Task 2: Is the phone number validation done in the frontend or backend? Is there already a field validation in place?
Code reviews are not a good moment to discuss architecture; having long discussions on PRs should be avoided. A way to prevent these long code reviews is to discuss the big decisions beforehand.
Finally, to make sure that the issue is clear, draw some diagrams or wireframes and validate them with a colleague or the issue's reporter.
During Development: Don't Bite More Than You Can Chew
In my opinion, the key to a good end is to split the task into smaller ones and make sure that each subtask is testable (not necessarily with unit tests). This way, the beginning is not so overwhelming, and we can check our progress as we go; instead of waiting until the last moment to test.
The same way you don’t try to eat your whole meal with one bite. Don’t try to finish your task at once.
It is also mentally gratifying to have small wins on the way. It helps you stay motivated and understand the progress you do. These small steps are important to keep a happy and encouraging attitude along the way.
There are many ways to split a task into smaller subtasks (to take bites).
Build an "MVP" of the Task
Strip the task into the core and only develop that; forget about anything extra.
- Task 1: All users have the same profile page with a hard-coded URL.
- Task 2: The user can add the phone number in the registration but cannot change it in the user profile.
Once we have developed this "MVP," we finalize the task. The main idea is to start coding and not feel overwhelmed by the job.
Build the "Happy Path" First
Forget about edge cases, weird scenarios, showing and managing errors. Instead, focus only on the task when everything goes according to the plan.
- Task 1: Assume that the user always has the avatar image.
- Task 2: Do not validate the phone number input.
Once we have implemented the happy path, we can start working on the edge cases, errors, and other scenarios.
Divide And Conquer
Like in recursive algorithms: divide the task and work on smaller and more manageable subtasks.
- Task 1: Create the UI in the navigation first, then get the user's information.
- Task 2: Add the phone number to the endpoint of the backend test it with Postman. Then add the field in the form. Finally, put them together.
Test each small subtask and never move to the next one until the subtask is completed.
Separate Views And Business Logic
For frontend tasks, we first implement the UI without any functionality, and once we have it, we add the logic. In the backend, we first hard code the new data in the response and then add the logic to build it.
- Task 1: Endpoint returns a hard-coded URL image with the user.
- Task 2: Create the phone number field in all the user forms.
Choose Your Own
It doesn't matter how we split the task into smaller tasks. We need to develop and implement all the required functionality in the end. Yet, by splitting it feels less overwhelming, and we remove the "blank page" syndrome.
For example, the subtasks I would do for task 1:
- Show first an avatar with a hard-coded image.
- Implement the dropdown with any text.
- Complete the dropdown with the required links.
- Fetch the avatar image from the backend. To test it, I would print it in the console.
- If the user avatar is present, use it in step 1 instead of the hard-coded image.
Don't Bit More Than You Can Chew
In the end, the advice here is as simple as the title: "Don't bite more than you can chew." When eating, you don't take a huge bite that doesn't fit the mouth. Or take a bite while still chewing (or you shouldn't, 😂).
Imagine the task as the whole meal in front of you. You approach the meal by taking small bites and chewing before swallowing. You learned that as a child and then kept learning with different meals.
A bite depends on the meal and your tools (fork and knife, chopsticks, hands). Yet, you know without thinking almost how big the bite should be. You have learned that by experience. When you were still a child, you learned by suffering the consequences of taking bites that were too big.
Learn to split tasks the same way you learned to take bites: trial and error.
After Development: Refactor
Once we have solved the problem and everything is working, we can refactor our code to make it cleaner and more readable. It's hard (impossible, I'd say) to write perfect code from the beginning, and that should not be the goal. The goal at the beginning is to solve the problem in any way possible.
When coding, we should be doing one of the following two things:
- Adding or updating functionality.
Never refactor at the same time that you add new functionality. This just calls for problems. It will be harder to debug if you change too much and the code does not work as expected.
In a nutshell, this is how I like approaching development tasks:
- Understand it and ask questions.
- Agree with the implementation approach if needed.
- Split it into subtasks: Don't bite more than you can swallow.
Now you are ready to submit the pull request.
If you like this post, consider sharing it with your friends on twitter or forwarding this email to them 🙈
Don't hesitate to reach out to me if you have any questions or see an error. I highly appreciate it.
And thanks to Bernat and Sebastià for reviewing this article 🙏
Thanks for reading, don't be a stranger 👋
GIMTEC is the newsletter I wish I had earlier in my software engineering career.
Every other Wednesday, I share an article on a topic that you won't learn at work.
Join more than 3,000 subscribers below.