Working With Legacy Code

I recently began reading the book Working Effectively With Legacy Code by Michael Feathers, and it has opened my eyes to a world of code that I have never encountered before, legacy code. While many people describe legacy code as code that is old and difficult to work with, Michael describes legacy code as a code base that is not supported by an automated test suite. Code without tests is legacy code because code without tests is difficult to change, and code that is difficult to change is legacy code!

From here the book goes on to describe that getting code from legacy code to clean code is a long process, and that even the best teams will take a long time to make that change.

After the preface the first chapter goes over the four reasons to change software. These four reasons are:

  1. Adding a feature
  2. Fixing a bug
  3. Improving the design
  4. Optimizing resource usage

The first two reasons are very straightforward because they are the main job of software developers. We create new features by adding or changing the behavior of the program. While it seems very obvious that this is what software developers do, the process of developing become much more difficult when having to work with legacy code. The legacy code makes it more difficult to add, change, or fix features because any changes that the developer makes could be affecting another part of the system, and because there is no test suite set for the application, the developer has no feedback loop to know if they are breaking the program.

The other reasons for changing the software are a bit different from adding a feature or fixing a bug because they should not change the behavior of the software, but will change how the software runs or how it is structured. These changes also require the code to be under test in order to make sure that the software does not change its behavior.

This book is packed with resources about how to approach a legacy code base and how to get it into a well tested state. I look forward to finishing it and writing more blogs about it!

Working With Mutable Hashes

Last weekend while I was working on a ruby project, I ran into an interesting issue that made me wish I was working with Clojure again. A part of this project uses hashes as a primary data structure, and I needed to filter some values out of that hash in order to only display certain objects. This led me into many issues where the hash that I was working with was being mutated by the methods I was running on it. At first this left me very confused as to why everything kept changing because when I was working in clojure, this was a thought I never had to have. I ended up getting it working, but the way I solved it wasn't the best solution.

While I could not get to a good solution on my own last weekend, another apprentice I was working with showed me how to write a method to copy the hash. This copy method used a ruby class called Marshal, a class that is used to serialize objects into data streams. By using the Marshal class to copy the hash I was working with, I was able to create a method that returned the copied hash to me, and then I could use this copied hash as an input to the methods that were mutating my hash.

working around a framework

After an intense first week of working on a new project, I am now feeling much more comfortable project, but reaching this point took a bit longer than I had originally expected. One of the reasons why I had a longer than expected ramp up was that I had to learn the quirks and domain specific language of a new framework. While the framework was not a new programming language, it did come with its own domain language that was a bit clunky, and was hard to follow for a few days. In order to understand the new framework we were using, I drew the relationships of each object and how they interacted with each other on a sheet of paper. This greatly helped me to visualize how I could find and reference certain objects from within the program.

After I got fully ramped up on the project, I began to tackle more difficult problems, and along with more difficult problems, many of the solutions to these problems were not well documented in the framework documents. This lead me to develop a new strategy for figuring out the more complicated framework issues, and by using this new strategy, I was able to find some fixes that were not well documented by the framework writers. Now when I encounter an issue, instead of first going to the docs to look it up, I go to the framework github page and search the repository for the object or method that is troubling me. Using this strategy instead of just reading the docs has allowed me to uncover undocumented default values, strange rescue values, and undocumented methods.

Having the opportunity to work on this project has given me a great opportunity to learn how to work with a large framework, and how to come up with elegant solutions when working with a rigid framework.

Preventing SQL Injection With Rails

After a few months of not working with ruby or rails, I am once again working in my native tongue, and it is an exciting experience to apply my new skills to the first language I learned. Today I worked on a story to improve a controller method to use one query instead of 3, and it required using some raw SQL in the code. This gave me a flashback to when I was in Dev Bootcamp working on the SQL injection assignment because whenever you use raw SQL in your code, you open yourself up to an attack known as SQL injection. SQL injection is a very simple attack on your system that occurs when a malicious user enters SQL into your program via one of your user facing endpoints (e.g. a search bar or url), and you in turn insert the malicious SQL into your program. An example of an unprotected query:

user_input = params[:input]

secret_data = SecretData.where("client_email = #{user_input}")

The malicious SQL works by first ending the programs existing SQL query, and then insert its own SQL statement that deletes all the data from the table. While this is an incredibly simple attack, there is an equally simple way to protect your program from such attacks, and in rails this can be done by parameterizing your query. An example of a parameterized query is:

user_input = params[:input]

secret_data = SecretData.where("client_email = ?", user_input)

This parameterized query prevents SQL injection by sanitizing the SQL statement, and prevents the statement from being closed by the input SQL string. By making this simple change you prevent your program from being attacked by a very simple yet dangerous cyber attack.

Hour of Code

Early this morning and before I even came into the office today, I got to participate in an activity called the Hour of Code at Well Academy High School here in Chicago. The hour of code is an event held across the nation where students of all ages participate in programming activities instead of their regular classes for one hour. It was a great experience to assist these teenagers take their first steps into programming.

The format of the activity was to spend an hour working through programming problems using visual code blocks, instead of written code, to solve a series of increasingly difficult puzzles. One of the first experiences I had with programming, was using a program called ALICE that worked in the same way as the challenges, and I was excited to help people take their first steps in computer programming.

While the event as a whole was incredible, a moment that stands out to me was when I helped a girl learn about looping. As I was walking around the room, I noticed that she had many repeated commands stacked on top of each other, so I sat down next to her and helped guide her to use a loop instead of the repeated commands. This was a really great experience to share the power of programming with someone that has had no prior experience with it.

I had an amazing experience at the hour of code this morning, and I look forward to working with the school more as they start their computer science program next year!