No one writes perfect code. The trick is catching errors sooner than later. Part of this is experiencing pain and self-correcting.

For example caching has burned me so many times that when I see an application doing caching I will triple check my solution because it might look good because it's the cache looking good, not your change, or vice versa.

In my opinion there are two approaches you can take.

Approach 1 - Wrecking Ball: Implement entire solution using dirty code. The point is to have the solution "working" and handling in the most likely use case. Then you start adjusting your code, abstracting, handling edge cases, dealing with error handling etc. This is like a depth first approach

Approach 2 - Radar: Implement the solution in layers. Like a radar scanner that goes from a center point out. You add code in layers, so first you get the inputs you need, then you do calcs, then work on errors. The key is test each layer, this is where unit tests become invaluable. This is a breadth first approach.

I am not a huge fan of pure Test Driven Development, but a hacky approach works well. I like writing code then testing. Now the test might be an automated test, or print statements, sometimes even visual.

Both approaches require a double check of what you have done. I am more of an approach #2 person. Mainly because my mind works like a weird neural network, I go depth then pull back and explore parallel avenues determining the best way to get to my target. Anyway, I digress.

The point I am trying to make is that regardless of your approach you have to test. Don't get stuck with test = unit test, but testing frequently and early is key. This can help with "2 days troubleshooting an error that you caused yourself"

The other hurdle you have is you are working on an existing project that you don't have experience in. The tribal knowledge is what gets you. Are you familiar with Cunningham's Law? To paraphrase, the fastest way to get an answer to a question is to post the wrong answer. Now something I have noticed is that if it's a Code Review, some people will just rubber stamp it, so they won't catch your errors and it will boomerang back to you. So I abuse human psychology instead. Mainly that people will pay attention if you use their names and ask for help.

It works even better for natural pessimists, but what I do is describe my code in words. Let's mock this...I'm going to use Groovy, mainly because I was Code Reviewing this change the other day.

def calculateDuration(start, end) {
long elapsedTime = end - start

Long second = (elapsedTime / 1000).longValue() % 60
Long minute = (elapsedTime / (1000 * 60)).longValue() % 60
Long hour = (elapsedTime / (1000 * 60 * 60)).longValue() % 24
Long remainderMillis = elapsedTime % 1000

return "${hour}h ${minute}m ${second}s ${remainderMillis}ms"
}

If I was writing the code, I would have pinged someone and said something along the lines of:

Hey XYZ, I need your help.

I am writing some code to easily be able to calculate the duration of things. I assume milliseconds as input, and a start and end time will get passed in.

Then I use that time to do an 'end - start' and figure out how many hours, minutes, seconds and milliseconds based on that result returning it as an "Xh Ym Zs Kms" string.

Anything I might have missed? Can you think of any edge cases that I might know about?

Thanks,

-----

Then if the discussion progresses I will point them to my specific branch. Programmatically we could debate the use of Long vs long. Or typing the input parameters and return or not.

The thing is this "solution" missed a key aspect. Milliseconds can't be assumed as input. He had tests, but all his tests were using milliseconds. If I had rubber stamped it as a code change, within three days we would have started getting messages from POs, and they would show up in Kubernetes namespace metadata, which if you don't know how things connect to each other, it would be hard to figure out why the metadata in a pod suddenly changed.