Project Management

A Survival Guide to Leading Software Projects (When You Don't Code)

From the The Young Project Manager Blog
by
Practical growth for project managers in the early stage of their careers.

About this Blog

RSS

Recent Posts

A Survival Guide to Leading Software Projects (When You Don't Code)

Why Volunteer Projects Build Stronger Project Leaders

Why Most Retrospectives Produce Documentation Instead of Learning

Why Projects Break During the Handoffs Nobody Owns

Why 31% of Complex Projects Fail to Deliver Their Benefits

Categories

Agile, Artificial Intelligence, career, Career Development, Career Development, Change Management, Education, Stakeholder Management

Date

linkedin twitter facebook Request to reuse this  


I read an article by Sean Goedecke, a software engineer who writes quite a lot about technology, engineering, and software design.

The title was already a small provocation: Software is slow because of bad design, not slow languages.

In other words, software can be slow because of poor design, and the programming language may have very little to do with it.

I found this interesting, especially because we are in 2026, when asking an artificial intelligence to rewrite thousands of lines of code in another language no longer sounds like such a crazy idea. Quite the opposite. It has become a very tempting possibility, and I wouldn’t be surprised to see someone proposing a complete rewrite of a system as the solution to a performance problem.

The logic seems simple, right? If a program written in Python is slow, and C can execute certain operations dozens of times faster, why not ask an AI agent to rewrite everything in C? Or Rust? Or, for someone feeling particularly adventurous, assembly?

Done. Problem solved.

The company saves money, the system becomes fifty times faster, users are happy, and the project manager can finally turn that status report green.

Wonderful!

Except it doesn’t really work like that.

Goedecke’s argument is that, in many systems, the real problem is somewhere else. And this is where I started thinking about something that, for me personally, has much more to do with project management than programming.

How often do we discuss a technically sophisticated solution before we properly understand the problem we are trying to solve?

A Ferrari won’t solve the traffic on the E6


I’ll use a comparison that is quite close to my reality here in Sweden.

For anyone who has to deal with traffic on the E6 towards Gothenburg, there is an important difference between having a fast car and getting to work faster.

If half of your journey is spent in a line of cars that are barely moving, replacing your car with a Ferrari won’t change much.

You might save a few minutes on the parts of the road where traffic is moving, of course, but the Ferrari will still be sitting behind the same truck, probably burning more fuel (and costing you a lot more money).

I think you get the idea...

Something similar happens in many software systems. A program can spend a considerable part of its time waiting for something to happen. Waiting for a database response, information from another system, communication over a network, or data being read from a disk.

Software engineers call these systems I/O-bound, meaning their performance is mainly limited by input and output operations. The other case is CPU-bound, when the main limitation is the processing work itself.

And this distinction changes the conversation quite a bit.

Goedecke gives an example where 50% of an application’s response time is spent querying a database. If you somehow managed to make the entire remaining part of the program execute instantly (which would be a pretty impressive miracle), the total response time would be cut in half.

So, in the best possible case, the system would become twice as fast. Nothing close to fifty times. And in practice, the improvement would probably be smaller.

Back in 1967, computer engineer Gene Amdahl published a paper about the limits of speeding up computing systems, which became the foundation of the famous Amdahl’s Law. The basic idea, to simplify things quite a lot, is that you can make one part of a program incredibly fast, but the overall improvement will still be limited by everything you cannot speed up.

And here I found an interesting connection with Eliyahu Goldratt’s Theory of Constraints, which became well known in management through his 1984 book The Goal. Goldratt explained how the performance of a process depends on its constraints, and why improving a step that is not the bottleneck may do absolutely nothing for the final result.

They are different concepts, of course, but I like how both lead us to a similar question: what’s the point of making something much faster if the thing that really limits the system stays exactly the same?

You can improve one part of a process by an incredible amount and still be limited by the time spent on everything that hasn’t changed.

It seems obvious once someone explains it, doesn’t it?

But this is where my mind started moving away from code, because this logic feels far too familiar to stay inside software engineering.

Think about approval processes inside a company. We can automate documents, connect tools, remove manual tasks, use AI to prepare reports, and make dozens of small improvements. But if an important decision still depends on someone who takes two weeks to answer an email, much of that efficiency simply disappears while everyone waits.

And this makes me think about how many projects are treated as execution problems when the real issue is that decisions are not being made. The team receives more tools, more meetings, more reports, perhaps more people, and the dependency that is causing the delay remains exactly where it was.

Maybe software architecture and the way we design organizational processes have much more in common than we might think.

Curiously, this reminded me of another discussion I recently brought up in an article about how to tell if your company’s next AI tool will end up costing more than it delivers.

We have an impressive ability to discuss tools before investigating the work we want to improve. With AI, that temptation has become even stronger.

But back to software, because there is another good story here.

The engineer who expected to lose 20% and gained 30%


In Goedecke’s article, I found a reference to a 2018 experience by Bryan Cantrill, an engineer with a great deal of experience in systems programming.

Cantrill was rewriting a program in Rust that he had previously developed in C, a language well known for its high performance. The problem was that the original program’s carefully improved design didn’t fit very well with Rust’s rules.

After trying to reproduce the same structure, he decided to write a simpler implementation, almost giving up on the original performance concerns. He expected the new version to be at least 20% slower.

Instead, it was around 30% faster.

Well, that was unexpected.

The interesting part is that this improvement didn’t happen simply because Rust was a faster language than C. Cantrill investigated the results and found that a large part of the difference came from the data structures used by the two implementations.

The C version used a binary search tree, and the Rust implementation used a B-tree, which in that particular case made better use of the processor’s memory cache.

For those who have absolutely no obligation to know what a B-tree is, the way the program organized and accessed its data made a bigger difference than the language itself.

And what I find curious is that this happened in the work of someone who knew very well how to write fast code. Of course, it would be a mistake to conclude that programming languages never matter. Sometimes they matter a lot, including for performance, security, and maintenance. Cantrill himself explains how Rust’s characteristics influenced the implementation and the structures he used.

But the story shows something interesting: even a choice that might look small from the outside, such as the structure used to store data, can change a system’s performance considerably.

I think this is a useful reminder for people who manage technology projects.

After all, we discuss languages, infrastructure, tools, and architecture all the time, but do we really understand which of these decisions make a difference to the problem we have?

So where does the project manager fit into all this?


This is the part that interests me most.

There is a very understandable insecurity among professionals who manage software projects but don’t come from a software development background.

The feeling that, to participate in a discussion about architecture, you must know programming languages, frameworks, development patterns, and every technical concept that appears in the conversation (preferably without having to ask what each acronym means).

And look, knowing technology helps a lot. I’m not going to try to convince anyone otherwise.

But I think there is an important difference between understanding a technical decision and being qualified to make that decision.

A project manager doesn’t need to choose which data structure an engineer should use. And certainly shouldn’t leave a meeting telling the team to abandon Python and start writing everything in Rust just because they read an interesting article about it.

That would be, at the very least, a rather questionable contribution.

On the other hand, our management responsibility doesn’t disappear just because the conversation becomes technical.

If someone proposes a complete rewrite of a system, we are talking about time, investment, risks, dependencies, maintenance, quality, and, most importantly, a promise of results that needs to be understood.

And the project manager needs to be able to participate in that conversation. This doesn’t mean questioning everything engineers say or thinking that every technical decision needs approval from someone in management. That would be a wonderful way to create yet another approval queue, exactly the kind I was complaining about a few paragraphs ago.

What interests me is the ability to understand enough to ask good questions, follow the alternatives, and help connect technical decisions with the project’s objectives.

Let’s go back to Goedecke’s article. When he describes the changes that brought real performance improvements in systems he worked on, we find things like storing the results of slow operations so they don’t need to run again (the famous cache), running slow tasks in the background, performing operations in parallel instead of one after another, improving database queries, and simply removing unnecessary work.

Notice what these solutions have in common. They all start from understanding where the system spends its time and how that work could be organized differently.

And a project manager can understand this logic without knowing how to implement any of these solutions.

In fact, I think this is one of the most interesting contributions we can make in a technical conversation: helping turn a proposed solution into a decision that makes sense for the project as a whole.

Because, let’s be fair, a proposal can be technically excellent and still be a poor decision for that particular moment in the project. The opposite can happen too. A less elegant technical solution may be perfectly good for what the business needs, considering the restrictions that exist.

Goedecke published another interesting article called What requirements don’t you have?.

I particularly liked his argument that, when designing a system, understanding the requirements you don’t need to meet can be just as important as knowing the ones you do.

What can be slow? What information can stay a few minutes out of date? Where can we accept less flexibility in exchange for simplicity? Which unusual situations really need to be handled?

Now, that is a good discussion to bring into a project scope conversation.

We spend so much time trying to identify every requirement, every expectation, every possible future need, that sometimes we forget to ask about the things we can consciously leave out.

And every piece of work we decide not to do, when that decision is properly understood, can reduce the complexity of what really matters.

But there is a problem with my defense of asking good questions. You can’t just go around asking about everything without making any effort to understand the answers.

That would be another wonderful way to drive engineers (even more) crazy.

So maybe it’s worth talking a little about what a project manager should study...

13 technology topics that can make a huge difference in your next projects


Well, if you don’t need to become a programmer, what should you study?

I think this is an interesting question, especially because there is an absurd amount of knowledge inside technology, and if you try to learn everything, you will probably end up knowing a little about many things without being able to connect them to the work you actually need to do.

Thinking specifically about people who manage software projects, I would start with thirteen topics. I’m not suggesting that you study each one until you can discuss implementation details with an experienced engineer.

I’m talking about developing enough knowledge to understand problems, follow decisions, and see their consequences for the project.

And the difference can be enormous… Let’s see:

1. Software Architecture

Understand how a system is organized, how its components connect, and why we have monolithic architectures, microservices, and so many other approaches.

When someone proposes breaking a system into several services, you begin to understand what that decision might improve and the extra work it can create.

It’s worth paying attention to coupling, which describes how much components depend on one another. Some dependencies can make it difficult to change one part of the system without affecting others. And this helps explain why an estimate that seems simple at first can grow quite a lot once the team starts investigating the work involved.

2. Cloud Computing and Cloud Services

AWS, Azure, Google Cloud, infrastructure as a service, managed platforms, serverless. We already have a nice collection of names to start with.

You don’t need to learn how to configure all of this, but you should understand what it means to run a system in the cloud, which responsibilities remain with the company, and how these choices affect costs, security, dependence on providers, and day-to-day operations.

One thing I find particularly important is understanding that using the cloud doesn’t automatically mean saving money. It creates enormous possibilities, but someone still needs to design, configure, monitor, and pay for the infrastructure being used.

And depending on the decisions, the bill can be quite a surprise.

3. APIs and System Integrations

An API is, to simplify things quite a lot, a way for systems to communicate with one another.
And that communication can fail, take time, or depend on third parties.

Understanding the basics of APIs, authentication, integration contracts, and communication between services helps explain why something that looks small can become a major dependency in your project schedule.

It’s useful to know the difference between synchronous communication, where one system waits for another to respond, and asynchronous communication, where the work can continue without waiting for an immediate answer.

Remember the traffic jam on the E6? Yes, it appears here too, though some waiting is unavoidable.

4. Databases and Data Flow

Where is the data stored? How is it accessed? What happens when information needs to move from one system to another?

You don’t need to write a single line of SQL, but understanding relational databases, NoSQL, migrations, and data quality can help a lot in discussions about estimates, risks, and changes.

This is especially true in projects involving older systems, because migrating data rarely means simply copying information from one place to another. There are different formats, business rules, relationships between records, and data quality problems that can make the job much more complicated.

And, as we saw in Goedecke’s article, the way data is accessed can have an enormous impact on performance.

5. Development, Testing, and Production Environments

A feature can work perfectly in a development environment and cause problems when it reaches production.
Why?

Because these environments can have different configurations, data volumes, integrations, permissions, and behavior. Understanding why different environments exist and which dependencies need to be checked can help avoid some very unpleasant surprises close to a launch date.

It helps explain why a team can say that a feature has been developed but still needs time to check how it works in the environment where people will actually use it.

6. Git, CI/CD, and Software Delivery

I know, this already sounds like an alphabet soup.

But understanding how developers control versions, combine changes, automate checks, and release new versions of software can change quite a lot in the way you think about deliveries, release frequency, risks, and planning.

CI/CD refers to continuous integration and continuous delivery or deployment practices, which can automate important parts of the development and release process.

It’s useful to learn what a rollback means, returning to a previous version when something goes wrong.

And to understand that going back is not always simple. Depending on the change, especially when data is involved, reversing it can require considerable preparation.

7. Software Testing and Quality Assurance

Unit tests, integration tests, end-to-end tests, automated tests, and manual tests. Each helps answer different questions about the quality of a system.

Knowing these differences helps you understand why a feature can be developed but still not be ready for delivery.

It lets you participate more effectively in discussions about testing strategy, acceptance criteria, risks, and test coverage. After all, no collection of tests can prove that a piece of software is completely free of problems.

And why can trying to recover a delayed schedule by simply cutting testing become so expensive? Well, I think that question deserves some attention before someone decides that quality is a good place to save time.

8. Performance and Bottlenecks

Now we’re back to the discussion that started this article.

Understanding latency, throughput, CPU usage, input and output operations, and how bottlenecks are identified helps you follow improvement proposals without depending only on someone saying that a certain technology is faster.

Latency is the time it takes to get a response. Throughput is the amount of work a system can process during a given period.

They seem like similar concepts, but they describe different things. A system can respond very quickly to one request and struggle when thousands of users start using it at the same time.

Sometimes the problem is the language. Other times, it’s the way the system was designed. And what matters is knowing how to find out.

9. Scalability and Capacity

A system that works perfectly with one hundred users may behave very differently with one hundred thousand.

It’s worth studying vertical and horizontal scaling, how systems handle peaks in demand, and why adding more infrastructure doesn’t always solve a capacity problem.

This discussion appears frequently in projects involving user growth, service expansion, or large volumes of information.

It affects costs, requirements, and planning too. Preparing a system for demand that may never arrive can be just as problematic as ignoring demand that is already coming.

10. Availability, Resilience, and Recovery

What happens when a service stops working? Is there any redundancy? How long can the system be unavailable? How much data could we lose without causing a serious problem?

Understanding concepts such as availability, disaster recovery, RTO (Recovery Time Objective), and RPO (Recovery Point Objective, related to acceptable data loss) helps turn technical concerns into business decisions.

These concepts can sound very specialized until the moment we need to decide how much money to invest in preventing an interruption.

Because keeping a system available almost all the time usually requires different investments and technical choices from running a system that can be offline for a few hours without serious consequences.

And the business needs to be part of that decision.

11. Information Security and Access Control

Authentication, authorization, encryption, permissions, data protection, and vulnerabilities.

I would give this topic particular attention, because security shouldn’t appear only during a review just before launch.

Knowing how to ask questions about access, information exposure, vulnerabilities, and responsibilities can already help quite a lot, without requiring you to know the technical details of every solution.

Security involves decisions about architecture, development, operations, and processes. There is little benefit in having very well-protected infrastructure if the permissions given to users are inappropriate, for example.

12. Monitoring and Observability

How do we know a system is working correctly after it has been released to production?

What can logs, metrics, and alerts tell us? How does a team find the source of a problem when several services are involved?

Understanding the basics of monitoring and observability helps you see that delivering software is just one part of the story. Someone still needs to identify, investigate, and fix problems when they happen.

And if the project hasn’t considered how the system will be monitored and operated, we might be delivering something that works but will be very difficult to keep working.

13. Technical Debt and Software Maintenance

Perhaps one of the most important topics for people who manage projects, because it involves decisions that often look good in the short term.

A quick solution can meet a need today and make future changes more difficult. Poor architectural choices, insufficient testing, old dependencies, or temporary decisions that were never reviewed can increase the effort required to continue developing a system.

This doesn’t mean that every technical debt decision is a mistake. Sometimes consciously accepting a temporary solution makes perfect sense.

The problem starts when nobody understands the cost of that decision or when something temporary is treated as if it were permanent.

And here we return to that earlier discussion about the requirements we decide not to meet. Sometimes doing less today is the best decision. In other cases, we are simply pushing a problem into the future.

The difference is not always obvious, and that is exactly why we should understand the consequences before making the choice.

Okay, but do I really need to study all of this?


Not necessarily. And definitely not all at once.

Notice how almost all these topics connect to things a project manager needs to discuss? Estimates, risks, budgets, dependencies, scope, quality, schedules, decisions, and future consequences.

And none of them requires you to know how to program before you start learning.

Of course, depending on the type of project, some topics will be much more relevant than others.

Someone working on a system migration will probably need to understand quite a lot about data, integrations, and cloud services. Someone managing the development of a digital product might find more value in studying architecture, quality, performance, and software delivery.

And here I see a very interesting opportunity. Instead of studying these topics as if you were preparing for an exam, memorizing definitions and acronyms, why not learn by looking at the systems we already work with?

Understand how they work, talk to the people building them, and try to connect each concept to a real decision.

By the way, this discussion about learning technology reminded me of something I wrote about the ten minutes of internet access I had each week when I was younger.

Ten minutes...

That was the free internet time I had before my computer course, and I needed to use every second to download as much material as possible to study later, at home, without an internet connection.

During the week, questions would come up, problems would appear, and I would try different things in my code. I wrote everything down, and by the time I finally had internet access again, many of those questions had already been answered through trial and error.

Today, things are almost the opposite. We have immediate access to courses, documentation, videos, books, communities, and now artificial intelligence tools that can explain a technical concept in several ways, answer questions, and help us experiment with different solutions.

I’m certainly not saying that having ten minutes of internet access was better, for God’s sake.

I would have loved to have all these tools back then. But there is something about that learning process that still makes sense to me: the question came from a problem I was genuinely trying to solve.

And maybe that is a very effective way to learn technology when we work with projects. You hear a concept in a meeting, realize you don’t fully understand it, study it, return to the subject, and start connecting the pieces.

In the next conversation, you understand a little more. Then another concept appears. And your knowledge keeps growing around real problems, without the need to master an entire subject before you can start using what you have learned.

Two books, a coffee, and a whiteboard


For anyone interested in studying a little more, there are two books I consider good starting points, though they approach the subject from different directions.

The first is Accelerate, by Nicole Forsgren, Jez Humble, and Gene Kim. The book brings together research on software delivery performance and organizational practices associated with better results. I find it particularly interesting for managers because it helps connect technology, ways of working, and business results.

The second is Fundamentals of Software Architecture, by Mark Richards and Neal Ford, which goes more directly into architectural decisions, different patterns, and the trade-offs that each choice requires. The second edition, published in 2025, covers topics including cloud, event-driven architecture, and generative AI. It’s a more technical read, but very useful for understanding how software engineers and architects think about systems.

And, if I’m being practical, perhaps one of the easiest ways to start is one of the simplest.

Invite an engineer from your team for a coffee and ask them to explain how the system you’re working on actually works, drawing it on a whiteboard.

Where does the information enter the system? What happens after a user makes a request? Which systems communicate with each other? Where are the databases? What happens when a component becomes slow or stops responding?

Of course, the first conversation will probably bring another twenty questions. And some answers will require more study, because complex systems are, well, complex.

But I think that exercise would help you understand your project far more than memorizing a glossary of technical terms without knowing where those concepts appear in real work.

And there is another benefit I find quite relevant. Understanding the system better can help you understand the people working on it, their concerns, their difficulties, and the reasons behind their decisions.

Something that, for those of us working in management, has a lot of value.

The question I couldn’t stop thinking about


Let’s return to Goedecke’s article, because I think I’ve taken a sufficiently long detour into how project managers can learn technology.

I started by reading about Rust and C, went through a Ferrari stuck on the E6, traveled back to 1967, returned to my old computer classes, and somehow ended up recommending coffee with an engineer.

Not bad for an article about software performance.

But there is a clear technical point in Goedecke’s argument: if the goal is to improve software performance, it makes sense to first measure what is happening and look for ways to improve the system’s design, before assuming that rewriting everything in a faster language will solve the problem.

This doesn’t mean rewriting a system is always a bad idea. There are legitimate reasons to do it, including maintenance, security, portability, or limitations in the current architecture. And there are situations where the language itself really is a performance bottleneck.

But we need to understand those reasons, because each one leads to a different conversation about costs, risks, and results.

What stayed with me after reading the article, though, was something else. One of the insecurities of managing technical projects is knowing that you don’t know everything the people around you know. And that insecurity can lead us into two rather bad habits: giving opinions about subjects we don’t understand or staying out of discussions because we’re afraid of asking something basic.

Maybe there is an important space between these two positions.

A place for someone who understands their own role, respects the expertise of the people at the table, but still wants to understand the reasoning behind a decision.

And that requires studying, of course. It requires curiosity and being comfortable admitting that we don’t know something. But it requires recognizing that our contribution to a technical discussion can come from a different kind of knowledge, from being able to connect a decision to the wider context of the project, its restrictions, and the results we are trying to achieve.

After all, nobody should expect a project manager to know more about architecture than the architect, more about code than the developer, or more about security than the person responsible for security.

But it would be rather strange to manage an entire project without having at least a basic understanding of what these people are building, which problems they are trying to solve, and why certain decisions matter.

So, the next time someone suggests rewriting an entire system to make it faster, perhaps it’s worth starting with a very simple question:

Okay, but where exactly are we losing time?

If there is a good answer, great. We already have an interesting conversation ahead of us.

If nobody knows the answer, I think we have just found a question that matters much more than which programming language we should use.

And the Ferrari can stay at the dealership. At least for now.

Posted on: November 04, 2026 09:00 AM | Permalink

Comments (0)

Please login or join to subscribe to this item


Please Login/Register to leave a comment.

"How much deeper would the ocean be if sponges didn't live there?"

- Steven Wright

Sponsors