DEV Community

Yodit Weldegeorgise
Yodit Weldegeorgise

Posted on

Your API Is Someone Else’s Dependency

When I was preparing for my Commit Your Code conference talk on common mistakes developers make when building Application Programming Interfaces, commonly called APIs, I started by looking back at a Java project I had worked on during a six-week cohort that was part of Commit Your Code.

While we were building that project, I had already started paying closer attention to some of the decisions we were making around the API, especially how we named our endpoints, how we handled errors, where validation was happening, and what we were returning to the client. The application worked, and we built a lot together in six weeks, but there were also things I felt we could revisit and improve.

I think that is true for almost anything we build, in software and in life in general. There is almost always room for improvement when we come back to something with more experience, more context, or simply a different perspective.

Then the Call for Papers (CFP) for the Commit Your Code conference opened. I was one of the early people to submit, and part of that was intentional because I did not want to self-reject. I knew that if I gave myself too much time to think about whether I was ready to speak at a conference, I could easily start finding reasons to wait, prepare more, or apply another year, so I applied before I had enough time to change my mind.

Another reason the topic felt right was because of what I had already noticed while working on the cohort project. There were API decisions I wanted to understand better, and there were areas I thought we could revisit. Not because the project was bad, but because that is part of building software. We make decisions based on the time, knowledge, and experience we have, and later we often see things we would do differently.

That became the foundation of my talk. As I started researching those technical topics more deeply, I became curious about how the same ideas showed up outside of our project. I did not want to talk only about naming conventions, validation, status codes, Data Transfer Objects, versioning, and error handling as technical best practices. I wanted real examples that would make those ideas easier to understand and remember.

So I started looking at real-world cases. That research led me to Apollo and Reddit, DoorDash, Facebook and Cambridge Analytica, Square, and several other incidents. The situations were very different, but I started noticing something they had in common: your API is someone else’s dependency. Once another developer, application, or business builds on top of an API, decisions made on one side can affect much more than the team making that decision.

Eight Years of Building, Then the Rules Changed

One of the examples I found very interesting was Apollo, a popular third-party Reddit application created by developer Christian Selig. Over the years, it grew from an application he was building into his full-time work, and by the time Reddit announced changes to its API pricing in 2023, Apollo had been developed for roughly eight years.

When the pricing was announced, Selig calculated what it would mean based on Apollo’s existing usage. Apollo had made around seven billion API requests in the previous month, and at Reddit’s announced price, he estimated that continuing to operate at that level could cost almost $2 million per month, or more than $20 million per year.

That made me look at API changes differently because from the platform side, the decision could be described as changing API pricing, while from Apollo’s side, the same decision affected whether a product that had been built and improved for years could continue operating.

The timeline was another important part of the story. Selig said developers had about 30 days between learning the pricing and when the new charges would begin, which meant he would have needed to rethink Apollo’s payment model, transition existing users, test changes, and get an updated application through review within that period. He ultimately announced that Apollo would shut down on June 30, 2023.

Before researching this, API versioning was mostly something I thought about from the technical side. We talk about keeping version one available while introducing version two, providing documentation, announcing when an older version will be retired, and giving developers enough time to migrate, but the Apollo example made the other side easier to see.

There may be years of someone else’s work sitting on the other side of that API contract. A change that takes one internal team a few hours to implement can create weeks of work for developers consuming it. A new pricing model can determine whether another product is still financially possible, and a short migration period can force another team to make decisions much faster than they expected.

This does not mean an API should never change. Technology changes, security needs change, products evolve, and businesses make new decisions. For me, the lesson was that how we introduce change matters just as much as the change itself. If people depend on what we have built, communication, documentation, migration time, and backward compatibility matter too because someone else may have built something important on top of what we created.

When Retrying Makes an Outage Bigger

DoorDash gave me a completely different example. This was not about intentionally changing an API. It showed how one struggling part of a system can create problems across many other parts of the product.

During a DoorDash outage on June 19, 2021, latency began increasing in internal payment APIs while resource usage also increased. Services used by Dashers started showing errors, and the system began experiencing cascading failures.

Retrying a failed request normally sounds reasonable because network problems happen and services temporarily become unavailable. Trying again can sometimes solve the issue without the user even noticing anything went wrong, but it looks very different when thousands of requests start failing at the same time.

Services depending on DoorDash's payment infrastructure continued sending requests after failures. Each retry added more traffic to systems that were already struggling, and DoorDash reported that calls to its payment systems eventually reached around five times the normal volume. The logic that was supposed to help recover from a failure was now adding more pressure.

What made this useful for me was seeing how something happening inside one part of the system reached people far outside that API. Dashers were affected, customers had difficulty placing orders, and DoorDash eventually stopped accepting new consumer orders while engineers worked to stabilize the system.

From the engineering side, there were several services, dependencies, resource limits, and retry behaviors involved. From the customer side, it was much simpler because they could not order food. For a Dasher, it could mean they could not accept work, while for a restaurant, food might already be prepared and waiting.

That is one thing we can easily forget when we spend a lot of time looking at architecture diagrams. Users do not experience our architecture. They experience the outcome. They do not know which service is unavailable, which database query became slow, or which internal API started returning errors. They know whether they were able to do what they came to do.

This also changed how I think about error handling because an error response does not end when the server sends it. Something on the other side has to decide whether to retry, wait, stop, inform the user, or use another option, which means the way we handle failures is also part of the experience we create for systems depending on our API.

The API Was Doing What It Was Allowed to Do

The Facebook and Cambridge Analytica example made me think more about how much data an API should expose and how far that access can extend.

Aleksandr Kogan created a Facebook application that collected information from people who used it. Under Facebook's platform permissions at the time, the application could also collect profile information connected to those users' Facebook friends, even though those friends had not interacted with the application themselves.

According to the Federal Trade Commission, the application collected Facebook data from approximately 250,000 to 270,000 people who directly interacted with it, as well as data associated with roughly 50 million to 65 million Facebook friends in their social networks.

A much smaller group of people directly interacted with the application, but because of the way the platform permissions were designed, the reach of the data collection expanded to tens of millions of people. That connected directly to another topic I was researching for my conference talk: returning database entities directly through an API.

Imagine an entity contains twelve fields, but the client only needs four. Returning all twelve may feel easier because we do not have to create another class, map fields, or write additional code, but now we have exposed more information than the client actually needs.

Maybe today those extra fields seem harmless. Later, one of them could contain sensitive information, another developer might start depending on a field we never intended to support publicly, or our database structure might change and suddenly the API response changes with it. The convenience is on our side, but the consequences can reach the people depending on the API.

A Data Transfer Object (DTO) gives us a way to decide intentionally what information leaves the application instead of exposing everything available in the database. In this context, it is not only about adding another layer to the code. It gives us more control over the contract we expose to another system.

Sometimes Nothing Is Down and the Experience Still Feels Broken

Apollo, DoorDash, and Facebook are big examples, but for my conference talk I also wanted something that almost anyone who has applied for a job could relate to. That is where an Applicant Tracking System (ATS) came into the conversation.

An Applicant Tracking System is software companies use to manage job applications, résumés, candidates, and different stages of the hiring process. If you have applied for several jobs online, you have probably interacted with more than one Applicant Tracking System even if you never thought about the software behind the application.

You find a job you want, start the application, create an account, upload your résumé, fill in your work history and education, and answer several questions. Then somewhere in the process, the application does not let you continue because maybe one field is missing, something is in the wrong format, or the page tells you there is an error without clearly showing where the error is or what needs to change.

I used a screenshot of an Applicant Tracking System experience in my conference slide because it made validation easy to relate to. From the engineering side, the system may actually be working correctly because the backend receives the request, the validation logic catches incorrect input, the database is running, and the server is healthy. Nothing has technically crashed, but the person filling out the application may still feel stuck because they cannot figure out what needs to be fixed.

That is why validation and error handling became more than coding topics for me. A useful error response is part of the user experience. If an API receives invalid input and only returns a generic message saying something went wrong, we have rejected the request but we have not helped much. If the response clearly identifies which field is incorrect and what is expected, the next step becomes much easier because the goal should not only be to reject bad input but also to help the person or system understand what to do next.

The Mistakes Started Looking Different

By the time I finished researching my Commit Your Code talk, I was looking at the API decisions from our six-week project differently. Inconsistent naming was no longer only about code style because inconsistency can make working with an API unnecessarily confusing and stressful for another developer.

Validation was no longer simply about where a Java annotation belonged because it affected whether the client could understand what was wrong and how to correct it. Error handling was no longer simply about choosing between Hypertext Transfer Protocol (HTTP) response status codes such as 400 Bad Request, 404 Not Found, or 500 Internal Server Error, because the response could influence what the system on the other side does next.

Returning database entities directly was no longer only an architecture discussion. It connected to privacy, coupling, and deciding exactly what another system should receive. Versioning was also no longer simply about putting version one or version two in a Uniform Resource Locator (URL), the web address used to identify a resource, because Apollo showed how much another developer may have built on top of the contract we are changing.

The API Has Users Too

When we design a user interface, we naturally think about the person interacting with it. We ask whether they can understand what they are looking at, recover after making a mistake, understand what changed, avoid seeing information they should not see, and complete a task without unnecessary frustration.

I think APIs deserve the same level of thinking. The person using the API may not be clicking a button and may instead be another developer building an application on top of our service, but they are still a user and they also want a clear, consistent, and as stress-free as possible experience.

We should think about whether they can understand the responses they receive, whether errors help them understand what needs to be fixed, whether they have enough time to adapt when we introduce a breaking change, and whether they receive only the information they actually need. We should also think about how changes or failures in our API can affect the systems and people depending on it, because developer experience eventually becomes part of someone else's product experience.

Your API Is Part of Someone Else’s Experience

The closer we are to a codebase, the easier it becomes to assume other people understand what we understand. We know why a field has a strange name, why one endpoint behaves differently from another, which exception creates which response, and which internal service depends on another service somewhere else.

The developer consuming the API does not know all of that, and they should not have to. They experience the contract we give them, which is why consistency, useful validation, clear error responses, careful data exposure, versioning, documentation, and migration time all matter.

After going through all of this research, one idea summarized it well for me: your API is someone else’s dependency. For Apollo, that dependency was connected to a product that had been built over roughly eight years. For DoorDash, problems inside interconnected services eventually affected Dashers, merchants, and customers. For Facebook, the way data access was designed allowed information connected to people who had never interacted with the application themselves to be collected.

For someone filling out a job application through an Applicant Tracking System, the result may simply be frustration because an error message does not clearly explain what needs to be corrected. The scale is very different, but the idea is similar because the API is not the end of the story. Someone is using it to accomplish something else.

That is why, after doing this research, I think one of the most useful questions we can ask when building an API is not only “Does the API work?” but also “Can the person or system depending on it succeed?” Your API may look like one endpoint inside your codebase, but to someone else, it may be an important part of what they are building.

References

Reddit / Apollo
Christian Selig. Apollo will close down on June 30th: Reddit’s recent decisions and actions have unfortunately made it impossible for Apollo to continue. Reddit, June 8, 2023.
Apollo shutdown and Reddit API pricing announcement

Square
Square Developer. Incident Summary: 2017-03-16. This was one of the additional retry-related incidents I reviewed while researching the talk. The incident describes retry behavior that allowed up to 500 attempts without backoff and contributed to pressure across Square services.
Square incident summary

DoorDash
DoorDash Engineering. Our June 19th Outage Explained. The postmortem describes payment API latency, cascading retries, traffic reaching roughly five times normal levels, and the effect on Dashers and consumer orders.
DoorDash engineering outage postmortem

Facebook / Cambridge Analytica
Federal Trade Commission. FTC Sues Cambridge Analytica, Settles with Former CEO and App Developer. July 24, 2019.
Federal Trade Commission Cambridge Analytica case release

Federal Trade Commission. FTC sues Cambridge Analytica for deceptive claims about consumers’ personal information. July 24, 2019.
Federal Trade Commission Cambridge Analytica case details

Applicant Tracking System example
The Applicant Tracking System example comes from the screenshot and personal experience I used in my Commit Your Code presentation rather than an external case study.

Top comments (0)