Abstraction Has Ruined The Junior SWE Job Market, Not AI

7 minutes • Jun 21, 2026

In 2022, after finishing the first year of my undergraduate degree in computer science, there was talk about how the job market for junior software roles was rapidly shrinking. The reason being offered at the time was that companies had hired a huge number of employees during the COVID pandemic and were now laying people off in herds. This was causing a saturation of experienced engineers in the market who were now competing for the roles of newcomers with no experience.

Fast-forward to 2024. ChatGPT was a hot topic across campuses. There was still talk about how the junior job market was being affected, but this time it was due to AI taking those roles. Never mind the influx of engineers from the massive layoffs that had taken place recently, the reason you were not being hired is because of AI.

Though I admit that both of these reasons contributed to the decline in entry-level roles, I believe the core reason is much simpler: abstraction. Most systems these days are built with layers of opaque third-party libraries. Personal projects do not provide enough context for how these are used in production environments, therefore beginners are under-skilled for developer roles. This issue is accelerated by smaller companies adopting practices of larger companies. Since their software environments use the same tools, the qualifications for developer roles are similar no matter the scale of the company.

Wrecking Balls as Hammers

Many products are now built on several layers of verbose and opaque technologies which require hours of practice to become sufficiently skilled at to then debug and build systems for real-world use-cases. These include, but are not limited to: frontend frameworks, backend REST API libraries, ORMs for translational mapping between databases and application code, and in-memory caching tools.

As a result, the requirements for a software role have ballooned. Gone are the days where you had to program hardware to solve a problem. Now, you are expected to debug distributed systems hosted on cloud platforms, set up fragile build systems that translate from one standard of a language to another, and determine which specific version of a dependency is compatible with another. Your time is consumed by busywork that mostly involves stitching off-the-shelf solutions together.

These tasks are done through opinionated frameworks, each created by some Fortune 500 company for their particular use cases. Even when all you need is to create a sign-up page and a backend that validates the user’s input, you are interacting with layers of clunky code. Developers are left to strong-arm these premade solutions into behaving.

If you are a company like Meta, who owns two of the most popular social media platforms handling billions of requests, you could argue why you would want to develop a framework like React. It solves your issue of needing to share state across multiple UI components and pages, and having to frequently update the DOM in complex ways. However, if you’re a mom-and-pop shop who just needs to display a handful of products on a webpage, you don’t need a multi-million dollar tool. For you, using React would be similar to using a wrecking ball to hammer a nail into a wall. A frontend built with HTML, CSS, and JavaScript will suffice.

For the server, you don’t need an autoscaling backend built on AWS. Depending on your traffic, you could host your server on a PC plugged somewhere on-premises. If you need to scale-up later, you can handle that then. Just because this method was used decades ago does not mean it isn’t a reasonable solution.

If a tech stack consists mostly of simple solutions, it will be easier to find a developer who can maintain it. Juniors who are well-versed in data structures, algorithms, and simple client-server architectural patterns will be able to do the job since they aren’t required to know any specific frameworks, libraries, or a swath of fragmented architectural patterns. It becomes easier for them to train for the entry-level roles from home and allows them to apply for a wider range of jobs. Having simpler tech stacks means finding more qualified juniors.

Why Would I Reinvent the Wheel?

That last section might raise some questions about reusability. It’s long been a point that developers should never have to solve the same problem twice. However, this has been misinterpreted.

I agree that the cognitive act of finding a solution shouldn’t be repeated, but the implementation of the solution should be owned by the engineer. For example, if someone in the past has developed an algorithm for searching through a sorted set of integers, you should look up their method and understand how it works. Then, you should write the code yourself instead of reaching for a library. This way, you could adapt the solution to your needs if needed, and you could also ensure the API will never suddenly change underneath you.

I admit, the more solutions you implement yourself, the more responsibility you take on; I would think twice before writing my own database. However, if someone was to argue that using a library always leads to less upkeep, I would disagree. Many third-party solutions break systems that rely on them. This could be due to changes in the API, data models, or underlying logic.

Most of the time, using a library or framework is an attempt by engineers to avoid doing their job of writing code, or an attempt by companies to cut development time to ship faster. The latter is more common, but the first is becoming prevalent as new engineers enter the market. An increasing number of rookies are reaching for a solutions created by other people. Many developers are also under time pressure from managers to deliver a feature, which increases the value of reaching for an existing tool. But by doing so, they inherit all the bloat that comes with it. Now your codebase is cluttered with indirection and hidden pitfalls because of logic you have no control over.

Instead, engineers could spend the extra engineering hours implementing their own bespoke libraries. Their code should solve the problems they have, with no regard for generalization to use-cases outside of the project. If their company makes multiple pieces of software with similar structures, they could reuse the library across their organization. However, this is where the reuse fixation should end. Adapting to further use cases will lead to fragility.

To foster this sort of thought process, engineers must not be discouraged from implementing algorithms from scratch. Many are scared into believing a problem is too difficult and numerous issues could arise. Hence they should check if other developers, who are supposedly smarter and more capable than them, have an offering. Over time, this culture has taught new programmers that they are inherently incapable of solving their own issues. This is a classic example of learned helplessness.

A problem might have many sharp edges, but the cognitive work of finding a solution has likely been done. A quick look through a good data structures and algorithms book, or a domain-specific book, can help you find it. This should be an engineer’s first instinct.

When you reach for highly generalized industry-standard tools, it makes maintenance of the system harder. The skillset of the developer responsible for its maintenance has to be vastly wider (and also shallower), making it harder to find someone qualified for the role on paper.

“Can’t we just use a solution for a proof-of-concept and switch it out later?” There isn’t ever time to go back and completely re-write the service. At that point, people develop a ‘if it ain’t broke, don’t fix it’ mentality. If you want to speed development along to build a proof-of-concept, implement a naive solution on your own. If a second version is needed, you can go back and expand upon the original. This way, you get rapid insights into product-market fit, have stability since you aren’t relying on third-party tools, and gain the ability to re-write the system better in the future.

Conclusion

I firmly believe that the first step to a better market for beginners, and a world of experienced engineers who understand their role, is to implement simple solutions for simple problems. Allow your startups to function off of logic you have written yourself. Reduce the barrier to entry for professional software engineers by having smaller codebases that do not demand experience with several different frameworks.

Many of the algorithms you need for a feature are already there. You should train your team to find, understand, and implement them instead of integrating existing libraries and frameworks into your codebase. As you grow, you’ll gain a better understanding of your product’s core strengths and where more engineering effort is needed. At that point, you can switch over to third-party tooling to handle parts of the code that offer the least value proposition. Just remember, every new tool you add is an additional qualification for an engineer to meet.