So I know the code quality course says sweat details for edge cases.
However what’s your perspective of a scenario where you as an api wants to handle every case, but it turns into a bunch of one offs in terms of code readability?
One approach is to go and gracefully align on a win win solution for client and server.
This area is gray and I want to do the right thing here.
Is the idea more around the communication with my teammates to resolve these edge cases than having every edge case addressed in every layer?
If you’re implementing an API and you’re deciding between a complex API with a simple implementation vs a simple API with a complex implementation, you go for the latter.
A simple example is the UNIX file system commands. A majority of people will use touch, mkdir, cd, ls and pwd. They’re simple commands but if you know anything about filesystems and OS, there are tons of edge cases and are super complex (what if you print pwd on a renamed/deleted directory? Etc.). OS’ have millions of lines of code, yet we interact with them through only a few commands. This is great design since it abstracts away any nitty gritty details about the OS from us. This means even a beginner doesn’t need to know about OS internals and can still interact with it.
So in your case, you should probably handle every edge case even though it might get a little complicated. With that being said, maybe talk to your teammates about the edge cases and if they say they’ll handle it, then that’ll save you some work, but if it’s vague about who should handle the edge case, then the API implementer should handle responsibility by default.
To mitigate the readability tradeoff, think about how you can group edge cases together. If they’re coming from a single source, is there a way to pre-process the input? If they’re coming from the same behavior, is there a common way to handle them? If they’re a mix, is there a way to decouple different edge cases? Sometimes it’s difficult to group edge cases and you’ll have to bite the bullet, but at least your team will be grateful that you’re doing the hard work and not them (also a great way to show off how good your skills are)
Read more of Philosophy of Software Design by John Ousterhout for more details.
Helpful Tarodactyl really nailed it here: Make things simple for the end-user.
When you're building an API, the end-user is other engineers. You want to make it so that they can access powerful functionality through a simple interface. They should be able to call your API in 10 lines of code, not 100+.
This is one of the core concepts behind a great engineer: They abstract away pain. They take something that is incredibly complex and expose something that is simple, elegant, and beautiful. All of the nasty complexity is abstracted away underneath the hood.
Anyways, let's talk tactics when it comes to API design. I am assuming a classic front-end product calling REST API endpoint use-case here. Here are 3 different levels:
Half of this api will not be used in 6 months due to a critical impactful migration.
This means that supporting legacy and v2 would mean there will be throwaway code in 12 months to be safe.
This pattern and advice you’re suggesting is the way to go because id give a great ux to both clients plus also at some point it turns into a snack task because no one will use them?
Interesting, the throwaway aspect makes things interesting. If you have high-confidence in the migration, then there's nothing wrong with half-assing the code. There are more impactful things to work on than throwaway code.
The tricky part is the confidence angle. I have seen migration take 2 years instead of 2 months. "Throwaway" code can end up lasting a long time, haha. The painful part is when people start building on top of and extending the throwaway code...
Also, if you have a bunch of edge cases, you can keep the code clean with good modularization. The simplest way is to move processing logic into separate methods. For example, you can have a dedicated method called validateInput() that checks for all the ways the input to an API can be malformatted:
The cool thing about having clear classes/methods with separation of responsibilities is that this makes your code quite easy to unit test, making the code quality even higher!