Building Tangible Tools Beats Algorithmic Puzzles for Career Growth
The subtle danger of "solved" problems: Why building real tools beats algorithmic puzzles for career growth.
This conversation with John Crickett, a seasoned software engineer and creator of "Coding Challenges," reveals a critical, often overlooked, truth in software development: the most impactful learning and career advancement come not from solving abstract algorithmic puzzles, but from building tangible tools that address real-world needs. The non-obvious implication? Many developers are investing time in skills that yield diminishing returns, while neglecting the foundational practices that create lasting value and competitive advantage. This discussion is essential for any engineer, from junior to senior, who wants to move beyond superficial proficiency and cultivate deep, durable expertise. It offers a strategic advantage by reframing how one approaches learning and skill development, focusing on practical application and system-level understanding rather than rote memorization or theoretical exercises.
The Illusion of Algorithmic Mastery
The prevailing wisdom in many developer circles, particularly those influenced by technical interviews and competitive programming platforms, emphasizes the mastery of algorithms and data structures. John Crickett, however, argues that this focus, while having its place, often distracts from the core of software engineering: building functional, useful tools. His personal journey, from a childhood fascination with AI to creating a newsletter that guides engineers through building their own Redis, Git clients, and shells, underscores a fundamental belief: practical application is the true engine of growth.
The "aha!" moment for Crickett wasn't a sudden revelation but a consistent, decades-long practice. He recalls his early programming days, finding textbook examples like factorial functions profoundly uninspiring. "I want to like something more interesting," he states, a sentiment many developers can relate to. This led him to games and, eventually, to building actual applications. His experience with Huffman compression, revisited in multiple languages since 1998, exemplifies this approach. It's not about achieving state-of-the-art compression, but about the process of building something real, shipping it, and learning the nuances of a language through practical application.
"You know, I hate opening a book and it'd say like like this factorial functioning I'm like I never factorial function I want to like something more interesting."
This preference for the tangible over the theoretical is precisely why "Coding Challenges" resonates. It taps into a desire to build, to understand how the tools we use daily actually work. The success of the newsletter, rapidly exceeding Crickett's initial goal of a thousand subscribers, suggests a widespread hunger for this grounded approach, a rejection of abstract problems in favor of concrete challenges that mirror the complexities of professional software development.
The Downstream Costs of Abstraction
A significant consequence of prioritizing theoretical problems over building real tools is the erosion of fundamental understanding. Crickett points to the shift from lower-level languages like C to higher-level, interpreted languages. While this abstraction has undeniably boosted productivity, it has also led to a loss of foundational knowledge.
"We keep reinventing the wheels because we don't understand some of those underlying concepts."
He uses the example of Python and Java, often categorized as "interpreted" and "compiled" respectively, to illustrate this point. The reality is far more nuanced: both involve compilation to bytecode and execution on virtual machines, often with Just-In-Time (JIT) compilers. This fundamental misunderstanding, he argues, prevents developers from fully appreciating performance characteristics and optimization opportunities. The "skill erosion" he describes predates AI; it's a gradual process that has occurred as the industry has moved up the stack, abstracting away complexities that were once central to development.
This is where systems thinking becomes crucial. When developers don't understand the underlying mechanisms--how memory is managed, what compilation truly entails, or how streams are processed--they are ill-equipped to debug complex issues or design truly scalable systems. The "wc" (word count) utility, a simple Unix program, serves as a powerful example. A naive implementation might load an entire file into memory, failing spectacularly with large inputs. Crickett's challenge extends this by requiring handling 100GB files, forcing a shift to stream processing--a skill transferable to network requests, pipes, and cloud queues. This highlights how understanding core principles enables robust solutions, whereas relying solely on higher-level abstractions can lead to fragile systems.
The Myth of the "Solved" Problem and the Rise of AI
The conversation also touches on the impact of AI on software engineering skills. Crickett asserts that software engineering has never solely been about writing code. The traditional lifecycle, from customer interaction to delivery, involved far more analysis, design, and testing than pure coding. He argues that the recent emphasis on bootcamps teaching "coding in 10 weeks" has fostered a misconception that software engineering is just coding, further contributing to the erosion of broader engineering skills.
AI, in this context, is not an existential threat to software engineers but a powerful tool that can augment their capabilities, particularly for those who understand the underlying principles. For someone like Crickett, who admits to struggling with front-end development (JavaScript, HTML, CSS), AI can generate presentable interfaces, freeing him to focus on the problem-solving and core logic he enjoys, perhaps in Rust or Go.
However, the danger lies in viewing AI-generated code as inherently correct or complete. Just as humans can have gaps in knowledge leading to hallucinations, AI can too. The critical skill remains understanding the system, the requirements, and the trade-offs--areas where AI can assist but not replace human judgment. The "coding agent" challenge, where an LLM is treated as a REST API, exemplifies this: understanding the interface, the JSON payload, and the underlying mechanics is key to leveraging it effectively.
The trend of skill erosion is further illustrated by the persistent relevance of older technologies. Crickett highlights the booming mainframe market and the continued demand for COBOL courses, alongside the vast world of embedded systems where C remains dominant and Rust is gaining traction. These fields, often overlooked in mainstream tech discussions dominated by web development, represent massive, growing sectors where fundamental engineering principles are paramount. The lesson here is that the "problems" we think are solved or irrelevant often persist, and a deep understanding of underlying systems provides a durable advantage, regardless of the specific technology stack.
Actionable Takeaways for Durable Skills
To navigate this landscape and build a resilient career, developers must shift their focus from superficial problem-solving to deep, practical understanding.
- Embrace Building, Not Just Solving: Prioritize projects that require you to build functional tools, even if they seem less glamorous than algorithmic challenges. This builds tangible skills and a portfolio that demonstrates real-world capability.
- Deconstruct the Tools You Use: Regularly ask "how does this work?" about the libraries, frameworks, and utilities you rely on daily. Seek out resources that explain their underlying mechanisms.
- Revisit Foundational Concepts: Dedicate time to understanding core computer science principles like memory management, compilation, and stream processing. Even a basic grasp can significantly improve your debugging and design capabilities.
- Understand the "Why" Behind Abstraction: Recognize that higher-level languages and frameworks abstract complexity for productivity but can obscure critical details. Be aware of what you're abstracting away and the potential downstream consequences.
- Leverage AI Strategically: Use AI as a co-pilot for tasks you find tedious or challenging (like front-end development), but never as a replacement for critical thinking and understanding. Focus on guiding the AI and verifying its output.
- Explore "Unfashionable" Domains: Consider learning about areas like embedded systems, mainframes, or even older languages. The fundamental principles at play are often highly transferable and in demand.
- Seek Out "Hard" Problems: Actively look for challenges that involve scale, performance, or complex system interactions. These are the scenarios where deep understanding truly pays off and creates lasting competitive advantage.