Let me be a feedback loop for you

(this post was written with the intent to give it to my current, and future, direct reports in an attempt to establish part of our relationship)

Every time we need to solve a problem (or implement a feature) the process is the same

  1. First we think the approach we are going to follow. This is an abstract flow that we believe will solve the problem.
  2. Based on that we divide our approach to one or more components (modules / classes / functions, whatever suits the size of the problem) and assign a certain behavior to each one of them.
  3. Finally, we start implementing each component.

Feedback loop

How do we know if something we implemented is correct?

By getting feedback. Based on it we make improvements and try again. This loop keeps us on track and allows us to deliver something valuable.

This is why we try to have frequent and short feedback loops. This way any corrections occur sooner than later and we don’t spend time and effort into something that might, potentially, thrown away.

Tech lead as a feedback loop

Looking back to the process of solving a problem we could say that two out of the three steps do have a feedback loop to helps us.

In the implementation step we can argue that the compiler / linter / etc gives us immediate feedback on what we wrote. We fix it and move forward.

For the behavior step we get the feedback through tests. Is the component’s behavior the expected one? How about the components API? Can we use it easily in our tests? Answering these question helps in having solid components.

But what about the approach step? This is where the tech lead comes in.

Right before writing any code, thus putting effort into something, prepare a simple list with the steps you are about to take and discuss them with your tech lead. If there is any feedback use it to improve your approach and have another meeting.

Is it time consuming?
No. In case the approach needs improvement after the implementation the time to fix it will be longer.

Do you feel bad on spending your TL’s time?
Don’t. It is, literally, their job to help you.

I think that data classes help in violating the YAGNI principle

Let me ask you something. You are the reviewer in a PR that creates a simple calculator which needs to know how to add numbers. Just that. There are no reasons to make us think that the calculator will need more functionality.

Despite that the PR includes a calculator that can add, subtract and multiply. What do you do as a reviewer?

I want to believe that you will, respectfully, discuss the removal of the extra functionality otherwise the calculator will violate the YAGNI principle and add (a) more code for the developers to maintain and (b) more ways to couple the project with the calculator. And all that with no immediate benefit.

Do we need all that functionality?

The same goes with data classes. There is no reason to have a class that can be uniquely identified by all of its properties if we don’t use its instances this way. There is no reason to have an extra getter for every property if we don’t use, extensively, the destructuring declaration. There is no reason to have a copy mechanism if we never copy instances!

If it is there it is going to be used

I recently removed the data keyword from one of our oldest classes and I noticed that many of our newest tests started to fail in compilation. The compiler could not find the copy method which was used to create dummy values from other dummy values by changing one property per test.

When to create a data class?

Here is my thought process when trying to decide the type of class I’ll use:

Q: Is this class anything but a domain entity or value object?

A: Then a simple class is just fine.

Q: Is this class a domain entity? Meaning that it can be uniquely identified by a subset of its properties (ex: an id)?

A: Then a simple class with an implementation of equals/hashCode will be enough.

Q: Is this a value object? Meaning that it can be uniquely identified by all of its properties?

A: Yes.

Q: How many properties?

A: One. Then a value class is a must.

Q: Are you sure its just one?

A: Turns out its more! We’ll use a data class.

Don’t do it for the test

Our test code is the first consumer of our production code. Changing the production code, in this case change/create a class as data, to write more quickly a couple of tests will result in having tests that can easily break every time the production code changes since the tests know too much about the code’s internals and not its behavior.

Don’t be afraid to throw your work away

There are times, especially in large code bases, that you might be working towards a solution and get stuck because of something that you did not foresee.
That was my case this past week. In order to unblock the development of a new feature I decided to change the API of some classes. The changes would make the integration with the feature much easier and intuitive.

I moved some code, deleted some other, made a few additions and after 3 days I had the API I aimed for. Unfortunately this new API, even though it was great for the new feature, it did not play well with a certain flow. A flow that was not affected by the old API.

In other words by fixing one thing I broke another. So I did the only logical think to do.. I deleted the branch I working on!

Always weigh things

I have to admit that deleting a piece of code that you have worked for hours is not an easy decision. Especially when it looks and behaves as you have designed it. The urge to keep changing things in order to make all flows work is quite strong.

This is where you have to weigh things. Is it worth the effort? Do we have the time to invest? Will the final code be clean, scalable, readable?

In my case the decision to move forward and try to include the broken flow would mean tieing things together (bad code) and also adding a couple more days of work (more time). It wasn’t worth it.

Clean mind

A benefit of throwing a solution is that you can now see the other routes that where there from the start but you were too focused to notice them. Be it that you are no longer occupying your mind with the previous solution’s graph, be it that you have to figure something out, almost always you’ll find another way to tackle things.

In my case the new approach was way simpler and easier. The old API was left untouched and the entire integration was achieved from a different point that up until the deletion I hadn’t given it much attention.

Micro throwing

Throwing implementations is great for small things too like functions or new classes.
Every time I develop one of them, if I start to feel that things are slowing down I don’t think of it much, I just reset --hard and start over (it always helps if you already have a couple of tests to back you up).
Having one route crossed out and knowing, at least some part of the solution, I find the second, third etc implementation to be much faster.

Code review: don’t just request a change

When reviewing a PR I try to elaborate on my proposals for two reasons:

1. Out of respect to my colleague

Every piece of code is part of an effort that took time and thought. Requesting a code change by simply asking it (i don't like it, change it) or by dictating it (do it this way) demotes all the work that has been done.

I figured that, since my colleague spent a few hours to come up with a solution I owe her/him more than a few seconds!

2. I solidify my knowledge or even better, learn something new

By trying to justify the reason behind a request I end up understanding better why I prefer a solution or a format over another.

When I provide an example or when I do a mini investigation to help me clarify why something must change I often find that (a) many things that I thought I knew were not as I had them in mind and (b) I now understand a concept much better because I had to write about it. You don’t master something if you can’t explain it!

PS: never forget that it might say “request a change” but in reality you start a conversation between two professionals that will eventually benefit of the project