To everyone who has ever touched code at Olin – and that includes MatLab – I am here to tell you that we do not learn how to code at Olin.
This statement is slightly shocking. I imagine that many of you, at this point, are violently disagreeing with me. Hear me out.
There are two main criticisms I have of people who code at Olin. One, we don’t test our code. Two, we don’t design and refactor our code so that it is ‘clean.’ I didn’t realize how important testing, refactoring, and design were until I interned at software companies. I believe that Oliners should make an effort to incorporate these practices into our work.
Unit testing is ensuring that units of source code (often a function/class) work as intended when isolated from the rest of the system. For example, if you define a function that is supposed to calculate the distance between two points in the Cartesian plane, one unit test would make sure that the function returned five when given the points (0, 0) and (3, 4).
You may laugh at this example and wonder why you need to write code to test it. Isn’t it insanely obvious? Everyone knows the distance formula!
Here’s the thing, though – you are going to test this code anyway. How many times have you printed out the result of a function call in order to make sure it’s returning the right thing? How many times have you copied and pasted function calls with different inputs? How many times have you graphed the output of ode45 to make sure that your derivative is calculating the right thing?
Why not take your manual, laborious tests and save them all in a file so that you can run them on a whim?
The benefits of tests are numerous. You document your code with examples of how functions and classes are supposed to work. You become more confident knowing that each piece of your code functions as intended in isolation. Because your tests can be run at will, you save time whenever you change your code (renaming a function, say) and want to make sure that nothing has broken. Employers love prospective employees who understand testing.
Most Oliners never learn that unit testing exists. Did you know that MatLab has a testing framework? (How much could well-written tests help on Mech:E problem sets? Or in ModSim?)
However, even when we do discover testing, we usually don’t spend the time or effort to learn how to do it. The norm should be tests for every line of code we write. But it isn’t.
Why haven’t I learned this at Olin? Well, I did. Kind of. Allen Downey taught a class called Software Engineering my sophomore year, and I was the perfect example of an unwilling student. Allen told everyone taking the class that testing was important, and I believed him. He even went so far as to write tests for us on some assignments. However, it was too late for a professor to change my coding habits. I learned how to code at Olin, had settled on my own personal set of coding practices, and was stuck in my ways. Testing was done manually. I didn’t write tests for my final project. I suspect that a lot of my classmates’ experiences are similar.
Improving coding practices at Olin is not going to be driven by professors. We, the students, have to collectively decide that we care about testing our code. Without widespread adoption and enforcement by the community, Oliners will continue to treat testing as an optional activity.
Clean code is a much more nebulous concept. The most important thing to note about clean code is that it is a process. Rarely will anybody sit down to work on a coding problem and write exactly what they need on the first try. Refactoring should happen every time a new bit of functionality is implemented.
Writing clean code requires deliberation. The goal is to make code easy to read and understand, easy to maintain, and easy to modify/extend. This touches on a wide range of issues; everything from how long each line of code is allowed to be (79 characters is a good number) to making each function/class do only one thing (the single-responsibility principle) to the use of design patterns (common architectures that are reused in many contexts). I am not an authority on this subject, but I would like to learn more about the area with my peers.
Unfortunately, in my experience, conversations about designing code rarely happen on a level deeper than, “We have to do x and y to produce output z. Let’s start implementing x.” Ideally, everyone working on a coding problem would challenge each other with well thought-out arguments about the best way to do things in any given situation. Not only will everyone learn a lot about code design, but the quality of code will be higher. I know professors appreciate code that is easy to understand. You will appreciate your own code if it’s easy to understand, especially if you come back to it after a long hiatus. Employers also value programmers with a proven track record of writing high-quality code.
There are a few theories I have about why we code the way we do. The primary culprit, in my mind, is Olin’s “just get it done” mentality. The sad fact is that classes only run for a semester at a time, so most projects are roughly three weeks long. This often leads to frantic hacking. People view testing and refactoring as impediments to rapid iteration and development. “I want to get this working before we do it a different way” turns into, “It works, we should move on to the rest of the project because it’s due in two days.”
Another part of the equation is online documentation. Online documentation is broken into two sections; how to do things and how to test things. Anecdotal and personal evidence suggests that Oliners rarely stumble across the pages that explain how to test and quickly leave if they do. Worse, the documentation that explains how to do things rarely, if ever, embeds relevant content about tests. As a result, Oliners have a skewed perception of the importance of testing. In reality, testing is just as important as functioning code.
The last offender I’ll address is the notion that refactoring, clean code, and tests aren’t the sexy part of engineering. To many people, the neat part of coding is solving a difficult problem. Anything that happens after solving a problem is project overhead, a chore that has to get done. The most interesting decisions about software projects often occur after a bit of new functionality is created. The question changes from “How do we do this thing?” to “How can we write this in a way that anyone could understand and safely modify?”
Olin’s website says that “Olin ‘engineer-innovators’ envision and deliver products, services and systems that transform the way people live on this planet.” What Olin’s website does not say is that the process by which products are created is just as important as the actual work that’s done. We need to learn both sides of engineering, the process and the content.
So what can we, as engineers who care about processes as much as functionality, do to improve coding at Olin? I recommend reading books about coding practices. The Art of Unit Testing by Roy Osherove, Clean Code by Robert Martin, and Practical Object-Oriented Design in Ruby by Sandi Metz can all be obtained online and are excellent introductions to good code practices.
Short of that, talk to me or anyone else who has experience in these areas (Allen Downey is an excellent resource). If you’re writing Python, take a look at the unittest and mock libraries. Check out MathWorks’ page on writing unit tests in MatLab. Start learning about these tools. Start thinking about how you can write clean code.
I can’t evangelize good coding practices alone. Let’s start a discussion about better code at Olin.