Edited By Siddhartha Reddy Jonnalagadda, PhD
Written By Hundreds of Parents
Welcome back! You’ve built an impressive toolkit. You know how to write code, organize data, and even build simple AI models. But in the real world, code is rarely a solitary creation. It’s part of a larger system, built and maintained by teams, and it needs to be reliable, easy to change, and able to grow with a project.
This book is a guide to the art and science of software engineering. We won’t just talk about what to build; we’ll talk about how to build it well. We’ll learn the principles that turn a one-time script into a reusable tool, and a simple program into a robust application. We’ll use the same patient, step-by-step approach that values a deep understanding of core concepts.
Think of it this way: In your last books, you learned how to craft a beautiful, functional chair. This book will teach you how to build a workshop, create blueprints, and use the right tools to build hundreds of chairs, each one perfect and built to last.
This journey will introduce you to essential concepts that will guide your next steps as a confident and thoughtful programmer. Let’s begin.
Writing code is one thing; writing good code is another. Good code is like a clear, well-written instruction manual: it’s easy to read, easy to understand, and easy for someone else to pick up and use. This chapter is about learning the first principles of writing code that is clean, clear, and durable.
1.1 Readability: Your Future Self Will Thank You
The most important person who will read your code is you, six months from now. And when you do, you’ll probably have no memory of what you were thinking when you wrote it. Readability is the practice of writing code that is as clear as possible, so a human can understand it without a lot of effort.
Variable Names: A variable name should describe what it holds. Instead of x = 5, use max_retries = 5.
Function Names: A function name should describe what it does. Instead of do_stuff(), use calculate_total_cost().
Comments: Use comments (#) to explain the "why," not the "what." The code already tells you what it’s doing; a comment should explain the reasoning behind a choice.
# A good comment explaining the 'why'
# We're using a low batch size to avoid out-of-memory errors on older GPUs.
small_batch_size = 16
# An unnecessary comment explaining the 'what'
# Adds two numbers and returns the result
def add_numbers(a, b):
return a + b
1.2 Modularity: Building with LEGO Bricks 🧱
You’ve already learned a core principle of modularity in your functions. Modularity is the idea of breaking down a large program into small, independent, and reusable pieces. When you’re building a big system, you don’t want to think about the whole thing at once. You want to focus on one small part at a time.
Think of a car. You don’t build a car all at once. You build an engine, you build a transmission, and you build a chassis. Each of these parts is a module that can be worked on separately. As long as each part works as expected, they can be assembled into a complete car. In the same way, good code is built from small, self-contained modules, each with a clear, single purpose.
This approach makes your code easier to debug, because if something breaks, you know exactly which module is at fault. It also makes it easier to test, because you can test each module on its own to ensure it works.
How do you know if your code works? You can run it once and see what happens, but that only tells you if it works for that single set of inputs. To be a reliable craftsman, you need to build with confidence. Testing is a systematic way of checking your code to ensure it works as expected, both now and in the future.
2.1 Types of Tests: A Safety Net
There are many types of tests, and each one provides a different level of confidence.
Unit Tests: This is the smallest and most common type of test. A unit test checks a single, isolated piece of code, like a function or a method in a class. The goal is to make sure that a small piece of your code works exactly as you expect it to.
Integration Tests: An integration test checks if two or more modules work correctly when they are combined. It’s like checking if the engine and the transmission of our car fit together and work correctly.
End-to-End Tests: An end-to-end test simulates a user’s journey through the entire application. It’s a way to check if the whole system works together as a single, cohesive unit.
2.2 Writing Your First Unit Test
We’ll use a popular testing framework called pytest. To get started, you’ll install it and then write your first test in a new file named test_my_app.py.
# To install pytest
!pip install pytest
Now, let’s imagine you have a simple function in a file named my_app.py:
# my_app.py
def add(x, y):
"""A function to add two numbers."""
return x + y
Now, we’ll write a test for it in test_my_app.py. A test function must always start with test_.
# test_my_app.py
from my_app import add
# This function tests our 'add' function
def test_add_positive_numbers():
assert add(2, 3) == 5
# Another test to make sure it handles negative numbers
def test_add_negative_numbers():
assert add(-1, -1) == -2
To run your tests, you’ll go to the command line and run pytest. The framework will automatically find your test files and tell you if they passed or failed.
2.3 Test-Driven Development (TDD): Write the Test First
Some programmers use a practice called Test-Driven Development (TDD), where you write the test before you write the code. It’s like creating a blueprint for your function before you start building.
Write a test that fails because the function doesn’t exist yet.
Write just enough code to make the test pass.
Refactor (clean up) your code, and run the test again to make sure nothing broke.
This cycle of Red, Green, Refactor is a powerful way to ensure your code is always clean and correct.
In the real world, code changes a lot. And sometimes, those changes can break things. Version control is a system that records every change you make to your code over time. It’s like a magical sketchbook that remembers every single drawing and lets you go back in time to any point. It’s an essential tool for solo programmers and an absolute necessity for teams.
3.1 Introducing Git and GitHub
Git is the most popular version control system. It’s a program that runs on your computer and keeps a history of your changes. GitHub is a cloud-based service that stores your Git projects (called repositories). It acts as a central hub where a team can share code and collaborate.
Think of it this way: Git is the tool you use to manage your local work. GitHub is the library where you store your work and share it with others.
3.2 A Simple Workflow
You’ll learn to use a few core commands to manage your changes:
git init: This command turns a folder into a Git repository.
git add: This command stages your changes, telling Git which files you want to include in your next save.
git commit: This command saves your changes to the history. Every commit has a unique ID and a message that describes the changes.
git push: This command sends your saved changes from your computer to a remote repository, like one on GitHub.
git pull: This command pulls the latest changes from the remote repository to your computer.
This simple workflow of pulling, coding, adding, committing, and pushing is the backbone of modern software development.
You’ve written your code, tested it, and saved it with Git. Now, how do you get it from your computer into the hands of a user? Deployment is the process of getting your application up and running on a server. Continuous Integration/Continuous Deployment (CI/CD) is a modern approach that automates this process.
4.1 CI: Continuous Integration
Continuous Integration (CI) is a practice where developers regularly merge their code changes into a central repository. A CI tool then automatically runs a series of checks, like running all your unit tests, to ensure the new code hasn’t broken anything.
A developer writes a new feature.
They push their code to the main branch on GitHub.
The CI tool (like GitHub Actions) automatically runs all the tests.
If the tests pass, the code is considered "integrated" and safe to use. If they fail, the developer is notified.
This ensures that the main version of the code is always in a working state.
4.2 CD: Continuous Deployment
Continuous Deployment (CD) is the next step. If your code passes all the CI checks, the CD pipeline automatically deploys the new version of the application to a server. This means that every time a developer merges a change, the application is updated without any manual intervention.
This process allows companies to release new features and bug fixes very quickly and with a high degree of confidence. It’s how a developer’s single line of code can go from their computer to a website in a matter of minutes.
Congratulations! You’ve now mastered the core principles of software engineering. You’ve learned how to write clean code, how to build a safety net with automated tests, how to manage your project’s history with version control, and how to automate the process of getting your code to the user.
This is a significant milestone. It’s a testament to your hard work and a powerful demonstration of what you can accomplish. The skills you’ve acquired in this book are the bedrock upon which you can build anything.
Remember that learning is a personal process, and it’s perfectly fine to go at your own pace. The most important thing is to stay curious and keep building. Happy engineering!