DEV Community

Cover image for Write Less, Fix Never: The Art of Highly Reliable Code

Write Less, Fix Never: The Art of Highly Reliable Code

Dhruv Agarwal on June 17, 2024

If you're a developer tirelessly pushing out new changes, only to be dragged back by errors in your past work, this post is incredibly relevant f...
Collapse
 
jayantbh profile image
Jayant Bhawal

That's when I started asking three questions for every feature I built:

  • How will I know it's working?
  • How will I know it failed?
  • How will I know it succeeded?

This part. 👏

Collapse
 
dhruvagarwal profile image
Dhruv Agarwal

🙏🏼 Jayant

Collapse
 
jhoger profile image
John Hogerhuis • Edited

"The simplest way to ensure your code works as expected is by adding unit tests."

I'd argue it's functional tests. They're less brittle, and more likely to provide confidence that the application does what it's supposed to do the way it's supposed to do it.

Unit tests make your codebase probably 10x larger or more. That's a lot more maintenance.

The argument is that unit tests allow you to make changes confidently because they will catch regression faults. So they would increase development speed. But the unavoidable counterargument is that with so much more code to maintain... code that is directly tied to the lowest level of code, binding to every interface, every type, every property creates so much inertia, how will it balance out?

And if you are really making big changes you may end up discarding a whole lot of unit tests as things get reworked. In which case, you ended up doing work that had no real longevity. Bad investment.

The balance I recommend is unit tests for hard fought, intricate internal business logic you want to lock in forever, and functional tests for everything else.

They're both really functional tests but unit tests can get you more solid testing on certain things, while being easier to write.

I wrote an emulator... I created unit tests for the CPU, and some hardware simulations. These are forever code but I want to ensure they stay working now and forever. Great investment. And they're fast and short... I can test every instruction at a very granular level without having the coding and execution overhead of other layers.

Outside the core of the emulation I expect a lot of churn. I want to detect memory leaks, 2nd level issues across modules, performance issues, basically stuff that unit tests won't see anyway. That stuff wants a functional regression suite.

Code that doesn't do something functional for a user and never prevents a bug is cost with no business benefit.

If your budgets and time are infinite, write all the units tests you want.

Collapse
 
eljayadobe profile image
Eljay-Adobe

I think both.

Functional tests ensure the product functions as expected. Unit tests (TDD-style) ensures the code complies with basic correctness.

Collapse
 
jayantbh profile image
Jayant Bhawal

That's a great take.
Love the balanced perspective on this, @jhoger!

Collapse
 
bengoldberg1 profile image
Ben Goldberg

Divide up your unit tests into two separate groups, development and release.

If you refactor your code and your release tests break, it's a problem.

If you refactor your code and your development tests break, it might be safe to change the tests instead of the code.

Collapse
 
alexandracm profile image
Alexandra

No... you double your work and you repeat yourself.
You just need to refactor the tests before the code.

Collapse
 
bcouetil profile image
Benoit COUETIL 💫

Thank you for sharing 😊

I would label "4. Always Answer with a Link" as a broader concept "always plan for being unnecessary to the project". It applies to more categories than the one you mentioned.

Keep up the good work !

Collapse
 
dhruvagarwal profile image
Dhruv Agarwal

Thanks for the input @bcouetil! I agree to your point.

The advice of sharing a link always just makes it more actionable :)

Collapse
 
pinotattari profile image
Riccardo Bernardini

I would add

if your language allows it, use contracts (pre- and post-condition, type invariant, and so on)

I just love contracts:

  1. They document the behavior of your function/procedure/method better than any comment because they are executable and, therefore, it is much more difficult that they get out-of-dated
  2. They are "implicit tests" of your function. Moreover, they refer to the "theoretical" behavior of your function and because of this they rarely changed. For example, if you have a package that implements an associative map (a very, very simple example), you can add as post-condition to your insert procedure that the key given to the procedure now must be present in the map and the corresponding value must be the value given to the procedure. This is something that must be true, whatever the implementation you choose, because it is part of the "abstract nature" of an associative map.
  3. They are bug sniper. If something is wrong in a function/procedure/method, an exception will be raised right away (if you activated contract checking, a nice idea during the development), circumscribing the troublesome portion of code.
  4. If your language allows it, you can check them formally, that is, you can ask to the checker to prove that given the pre-conditions and the procedure body, the post-conditions follow. If the checker succeeds, you know that the function behaves as desired (or, at least, as specified by the contract).
Collapse
 
baasmurdo profile image
BaasMurdo

Great article. In theory, I agree with 99% of what was said. Unfortunately, this is only in theory. There are many companies where there simply is no time to do all of this. There are companies where results and getting the latest features out on a hard deadline is the top priority and as soon as that is done, there is another feature that is expected to be done ASAP as there is a hard deadline and so the cycle continues, this is very common for startups that have investors and a board to keep happy.
Trying to convince board members who are not tech-savvy that 50% to 70% (a personal guestimate) of development time will be tests and documentation is going to be a hard sell.

Collapse
 
dhruvagarwal profile image
Dhruv Agarwal

Great point @baasmurdo! That's a change that tech leaders have to drive with other stakeholders not by hiding but by educating.

In one of my EM stints, I literally did a tech process onboarding for non tech joiners so that they understand what our estimates meant and what all a developer does apart from just writing code.

Collapse
 
lovetrax profile image
Matt Trachsel

I feel compelled to point out to any newer devs that this sentence "Shipping code without testing all the edge cases is like a spray and pray strategy" is nonsense. It is literally impossible to test every possible edge case in an application with even a tiny amount of complexity. Your applications WILL fail, you should test the most complex/important/used parts of an application. And especially where those labels overlap. 100% code coverage is NOT a codebase with no errors.

Collapse
 
dhruvagarwal profile image
Dhruv Agarwal

Thanks for the candid comment @lovetrax! While it is difficult to write tests for every possible edge case but it is indeed possible to define a scope relevant to the business/product and test for those cases. That will make the code robust for the defined scope.

When things out of your scope start to break, you can take an educated call to include it or ignore it 😊

Collapse
 
adnanhashmi09 profile image
Adnan Hashmi

This is a really good read. Lately I have been a victim to reacting to bugs 😅. I will start asking those 3 questions on everything I build from now on. It seems like a really good way to avoid bugs early on and save those tens of hours of debugging. Thanks!! 😊

Collapse
 
shivamchhuneja profile image
Shivam Chhuneja

interesting read Dhruv!

Collapse
 
dhruvagarwal profile image
Dhruv Agarwal

Thanks Shivam!

Collapse
 
vipindevelops profile image
Vipin Chaudhary

Thank you for sharing such valuable insights!

Collapse
 
dhruvagarwal profile image
Dhruv Agarwal

Awesome @vipindevelops!

Collapse
 
natisayada profile image
NatiSayada

I like it!
I would also add, always ask a question with a link

Collapse
 
dhruvagarwal profile image
Dhruv Agarwal

Interesting take @natisayada!

Collapse
 
anilsonix profile image
Anil Kumar Soni

Great article

Collapse
 
dhruvagarwal profile image
Dhruv Agarwal

Thanks @anilsonix! Glad you liked it

Collapse
 
campustrack profile image
CampusTrack • Edited

Awesome post, thanks!

Collapse
 
joel_moran_0c080db88fa292 profile image
Joel Moran

Wonderful article with very practical tips and how-tos! Thanks a lot for posting and sharing!

In class today, my classmate asked if working with fellow developers is typically characterized by a very cooperative and supportive atmosphere.

I guess the word spread already. "No one can do it all alone." We are all in this together.