12 Jan 2015
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:
- Adding a feature
- Fixing a bug
- Improving the design
- 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!
06 Jan 2015
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.
30 Dec 2014
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.
15 Dec 2014
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.
09 Dec 2014
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 code.org 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!