DEV Community

Frodo Alaska
Frodo Alaska

Posted on

The Failures Of API Design

It would be very easy to come on here and write about my absolute hate for modern APIs and how I completely despise them. I could tell you about the terrible state of modern APIs and how we got to this point. I love drama. I can go on about this for hours... but I will not. While sure, there will be a tinge of drama, my main focus here will be on the failures of these APIs rather than going on a tirade about them. So, without the need to stall, let's get riiiiight into the post. Sorry about that. Oh just a note, though. When I'm talking about "APIs" I mean libraries and/or frameworks. Keep that in mind. So if you think that this doesn't apply to your project because it's a "framework" not an API, I would advise you to go see a doctor because you clearly have a lot of brain damage. Okay now let's start.

The Ghost Problems

Why would any software developer use an API? Well, it's not to get rid of that shirt stain you had for the last three days that's for sure. Instead, we crazy bunch use APIs to progress our software development at a faster rate. I don't really want to work with the Windows API nor do I care to open the rotten can of sardines that is the X11 API. But, thankfully, I don't have to. There are plenty of APIs that handle that for me. And they handle it very nicely too. GLFW is one of these APIs. Easy to use, fast to set up, and overall doesn't have any overhead. Handles window creation, input, and any operating system-specific stuff. It solves a clear and appropriate problem. Left pad, however, is the complete opposite. Does it solve a problem? Sure it does... if you were hit on the head with a baseball bat 17 times. Can't I at least add padding to any direction? No? Does it just have to be the left? Even though we C++ folks can say that the JavaScript weirdos are the only ones who would do such a heinous thing, that wouldn't be the entire story. It would be hypocritical even to assume that all of the useless APIs exist only in the JS ecosystem. Although being hypocritical is my strong point, even I would stop you right there. If there is an ecosystem that would be perfect for breeding unnecessarily complex, widely inefficient, and completely useless libraries, it would be C++... Rust would come at a close second but C++ is the mother of all useless and complex libraries.

I pray for the soul who has tried to go into the STL code and read it. I envy those who haven't seen the pile of dog shit that is the Standard Template Library. However, STL has a purpose for its existence. Perhaps there are no justifications for its complexity but hey it gets the job done pretty well. It saves a lot of time and effort. And that's what APIs are for. To save time and money. Even though we can make fun of left-pad all day every day, we can't ignore the fact that it's a small API that would not cause any harm. However, there are a slew of APIs that do not contribute to the ecosystem in any sort of useful way. Yet, these APIs get used by people like you because you're too busy jerking yourself off under the table rather than adding a bit of padding to the left. There's nothing harmless about trying to create an API much like GLFW. Perhaps you really like math and you would like to make a math library specifically for games. Is it going to be the most performant math library out there? Probably not. Is anyone going to use it for their project? Maybe. Who knows? But that doesn't matter. You made something that you enjoy and you learned a lot from it. No one can take that away from you. However, the problem arrives when you try to make a library that has absolutely no reason to live and market it to programmers. Some programmers might have eaten from the same bogger restaurant that you go to every day and actually use your useless API.

Let me ask you a question. If I didn't use your API in my next project, how fucked will I be? Is your API going to save me time? Is it going to save me from debugging some annoying bug? Is it going to help my crumbling marriage? If your immediate answer to all of these questions is yes then congratulations! You're API is useful! On the other hand, if the answer was no or you tried to twist the question to your liking, then I have news for you, buddy, you have a very rare syndrome called Bjarne Stroustrup.

Don't try to solve non-existing problems. Make an API that solves real problems that thousands of programmers have to deal with every day. Can't find these problems? Then go and make projects. Create projects that you actually enjoy in the real world and you'll discover an array of these problems all the time. Why do you think the creators of GLFW created it in the first place? Because they probably were sick of programming for the Windows API and the X11 API and whatever other OS-specific API all the time. A problem exists and you come in and solve it. Like stealing money from my ex-wife. Easy.

The Adventure Is More Fun Than The Journey

While that might be a true statement if you were traveling to East Somalia with your imaginary girlfriend, it is not the case in API design. Too many programmers think about the implementation rather than the actual API. They think about algorithms and performance rather than the naming convention. Don't get me wrong, those things are very important. I'm not saying they aren't. Yet, focusing on them too much will make your API look and feel unprepared. Functions that take in multiple parameters, types that don't make any sense, absolutely no documentation (I'll definitely get to that later), and just a general lack of care for the design.

While this is not an API, Gimp is a very powerful photo editing tool. I use it daily for different tasks. It's free, robust, and open source. You really can't go wrong with it. Yet, the UI is worse than that day you spent with your uncle back in 1998. The UI is extremely unintuitive, bland, hard to look at, and hard to navigate.

Listen, I get it. I hate UI. I can't stand it. That's one of the reasons why I didn't want to be a web programmer. Well, that and the fact that I hate money. But, for the love of Bill Gates's knees, when you're trying to ship a product where the main focus is UI, then you at least need to put some effort into it. Take a UI/UX course. Talk to your UI friend and let them help you. Let's be honest, though, you probably don't have friends. But do anything I don't know.

At the end of the day, the users who will use your API and/or application don't care about how you implemented a brush that draws nudes. Maybe the programmers using your API will care about the performance but many of the same programmers will be encouraged to use another API if what you created is completely hard to work with.

I can give many examples of APIs that have this exact problem... but I won't. Instead, I'll be making an example of myself. Long ago (like, maybe 4 months ago), I made an API called Gravel. It was supposed to be a game framework that abstracts away all of the window creation code, input handling, rendering with OpenGL, and so on. I thought I was the king. I was getting ready to become bald, wear glasses, and call myself Bill Jobs III. I thought the API was so good. I thought it was so good, in fact, that I decided to use it and create a game. The game was called The Problem Solver. The game was supposed to be simple. I made a 3D game from scratch before. Pfft. How hard can it be? Especially now that I have this amazing API that I created... I hope you can tell there's gonna be foreshadowing here.

Not only did it prolong the development cycle of the game, the API also went out of its way to fuck every single little gameplay feature I was adding. It was a nightmare. Besides the fact that the API's design itself was retarded beyond belief, there were a lot of spelling errors, functions that didn't work the way they were supposed to, and many more fun adventures.

I never really thought about how the API would be used. I never really thought how convoluted and unnecessarily complex everything was. I just thought about the implementation. How can I load an OBJ file? How can I draw text? How can I make a resource manager? Instead, the questions I should have been asking were how would the user load OBJ files? How would the user handle resources? How would the user use the API?

While I still think that excessive planning of a project without any coding being done is completely wasteful, I also think that you can't go into a project with absolutely no planning whatsoever. Perhaps that would be true if you were just testing the waters and seeing if something works or not. However, that is not the case when you're planning to release an API to the public for people to use.

No planning will lead to a terrible API design. Yet, no planning can also lead to a bloated API

Doing Everything That I Can Do

Having an all-in-one API that does everything sounds pretty neat. I wish there was an API that could load JSON files, render pixels to a screen, fix my libido, and make me a cup of tea. That would be great! Or would it? Welcome into another episode of trying to break your dreams and increase doubt in yourself.

Let's take a look at an API that I extremely love and enjoy working with, stb. And specifically, stb_image. This is a library that will help you with loading image files like PNG, TIF, JPEG, and so on. It is a necessity if you're trying to make a game engine or any photo-related programs for that matter. It has one purpose: give you the pixel data--plus some information like the width and height--of the image loaded. That's it. Does it try to add some nice effects? No. Does it try to write pixel data to an image? No. Stb has another library for that single purpose. Does it try to sleep with your mom? No. That's my purpose.

Let's take a look at another API. Raylib. Now Raylib does a little bit more than just load images. Raylib is more of a game framework than a single-purpose library. It will create a window, handle input, load images, render pixels, handle loading and rendering fonts, and so on. Even though Raylib does multiple things, it doesn't go overboard. It tries to be simple yet robust. There's a clear vision for the library. They didn't try to force a GUI editor into the API or an alternative string type. It had one goal and that is to become a game framework. What happens when Raylib has added all the features it needs to add? Well, call it feature-complete and just move on. There's no need to bloat an API for no reason other than you "felt like it".

Now, let's take a look at an API that is worse than the devil: boost. What is boost you might ask? Everything. Boost is you, me, and that guy over there probably. Is it an API? A standard? A committee? I don't really know. Once you start losing vision of what your API truly is, you start to lose interest as well. How many times have you tried to include an API into your project only to discover it has a buttload of dependencies because the API does 1001 things at once.

Besides these bloated APIs being slow and too large to even add to your project, they would also lack the efficiency and performance of other APIs that decided just to stick to one thing. Jack of all trades, master of none. Stb is just a single header file that I can easily add to any project. Why would I not use it and instead opt to use another library that uses thousands of dependencies and does way more than I'm asking for?

However, not everything is black or white. Not too much is bad, yes. But, also, doing too little is bad, too. If you're API just adds padding to the left of a UI element then I don't really need to use it. I'll just do that myself. But sending and handling HTTP requests? Well, that's a different story. Adding APIs/dependencies in the C++ landscape is a hassle. You'll have to think about every dependency you're adding since it'll be something that you and the user will have to deal with. I can't just do "npm install" or "dotnet install" and everything will work magically. This is not a kid's programming language. This is a language for the mentally-derained. We like to torture ourselves.

Just don't use Boost. Please.

Not A Word Spoken

Would it hurt your fingeys so much if you tried to write what this function does and what the parameters mean? It probably won't. It'll probably take the same amount of time as when you're searching for weird hentai porn that you watch daily. Or just about longer than you last in bed.

I'm a strong believer in documenting with code. Meaning, instead of writing comments, you actually make your code sensible and understandable. So instead of writing, V2 V2a(V2 n, V2 m) which you would if you were hit by a bus full of cement that gives brain aneurysms, you can just write, Vector2 vector2_add(Vector2 v1, Vector2 v2). See how easy that is? Simple, straight to the point, and doesn't need a single line of explanation. Always remember that code lives, but comments do not. You might change something in your code but forget to change the comments, confusing the user completely.

However, that might not always necessarily be the case. Sometimes, there's just code that is hard to explain. You can't understand it immediately by just looking at it. Even if you try to make the names of the variables, functions, and types quite cohesive and sensible, you might still need to explain your code using comments. For example, here's a function definition from the GLFW library: GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share)

Even though the function might look sensible, there are still a couple of missing pieces here. What's a share? Why do I need to pass in a monitor? What does this function return if GLFW fails to create a window? Are there any errors that will be printed? Since GLFW is documented thoroughly you can easily find that out by just going to the function's definition (did I fail to mention that I'm using NeoVim btw?). However, not all APIs have the same privilege. You might find a library, for example, that has the function gymagf(). You'll get the wrong idea. All you have to work with here is a comment that says, // get string. What does that mean? What can you do with this? Why is Jared naked? Then you discover that it's just a function that returns a string with the value "Give Your Mom A Good Fig".

As with everything in the world, this isn't really a one-and-done answer. If you don't document your code, then you end up losing and confusing the user and even yourself if you haven't used the API in a while. Yet, when you document everything you end up putting a huge load (heh) on yourself. You need to find a fine line between what's enough documentation and what's excessive documentation. That's a boring answer I know but it's the truth.

It Works On My Machine, Though

Even though most of the points above can be applied to any ecosystem of any programming language, this point is just specific to C/C++. And don't worry, it's going to be brief believe me... just like your mom--okay, I'll stop.

It's no secret that the building and compilation process in C/C++ is just awful. These languages have no build system at all. We might have a cheap "build system" that we hate called CMake. But that's not really a "build system" per se. It was made so you didn't have to work with Makefiles and Make was made so you didn't have to work with raw CLI commands. The build system scene in C++ is truly heinous and needs to be put down ASAP (if I hear anyone of you saying to move to Rust I swear I'll stick a needle so far up your asshole...). But, in the meantime, it's the only thing we have. Therefore, trying to set up nice building instructions for C++ APIs is quite a hassle.

Not only do you have to think about which build system you'll use (believe me, it's not always CMake), but you're going to need to think about being cross-platform too. What if there's a user that wants to use your API on Windows? What are the building instructions there? What about Linux? Mac? Who uses a Mac anyway? You can't just expect everything to work because it worked on your machine. You need to test it. A lot. Many times. With different machines, with different projects, with different settings. You need to give it care since you're going to be delivering a product to people. What would people think about your API when they try to build it for the Apple 2 and it doesn't work? You have a terrible reputation in real life you don't want to ruin your digital reputation as well.

This is one of the points where there aren't any gray lines. It's just completely on one side. I'm not saying to support every platform out there. But do give some clear and simple instructions of how one would use your API and include it in their projects.

The Final Verdict

Listen, at the end of the day, I'm not your dad (unless you want me to be). You can do the opposite of what I've been saying here. I don't really care. It's just a frustrating problem that I've seen being done over and over again. There are a lot of points I even left out for the sake of brevity. For example, how these API creators don't really test their APIs in real applications and instead opt to show off the API "working" in some dummy project. Or how a bunch of APIs force you to use a coding style that you might not be familiar with or you don't enjoy.

There's a lot I could have said but, really, at the end of the day, you're going to end up jerking off under that table and leave your API looking like a 2$ toy from the local garage sale. So hey, you do you. Just please don't use Boost.

Top comments (0)