I’ve wanted to for a long time share my own journey through the “art” of versioning. Detailing how I’ve struggled with folder systems, and finally achieved enlightenment when I opened my github account.
But along the way of writing my story, I realized something much more profound. I realized that what we do today requires a minimal amount of actual logic that’s planted like a seed within a well-kept garden of dependencies and declarations.
The idea of versioning software dates back decades to RCS and every developer out there has an intimate story that tells how they became involved with Git, SVN, and the like. Because none of us ever start out committing.
I am one of those that has a history with versioning but my story goes deeper than just git commit -am “initial commit”. It goes much deeper and explains the shift in my thinking over the past year, and the thinking of others as well.
First, let’s consider what versioning and version control is good for. Version control is a simple way of making a history for your project and creating back ups. It’s a modern way of dating your documents and code and going back to _DRAFT3 or REVISED_CODE_2 when necessary. That’s how it started and from the outside, versioning looks a lot like that.
When I first started coding WordPress sites about five years ago, I considered version control necessary only for large sites and large projects where one has to jump around a lot. It never occured to me to use it on anything smaller and so I never applied it. It took me a couple of years to get a Bitbucket account and use it.
But what of it, then? It was just a “check in” type of place. I never took the addition and committing too seriously. I was a one man team so my commits were reserved to initial commit, scaffolding, done, and revisions. Titles worthy of tags, not commits. My theory on the subject back then was that I had found the best way to do “back ups” and never considered usefulness beyond it.
A year later, I found myself creating my github account but again, it was not for “version control” per se but rather to show off my work. You gotta add and commit those features to show your progress. It wasn’t for me, it was for others. I was out of the equation. And you can see it in my projects.
Yet, a fundamental shift in my thinking occured when I first started working with others via SVN. Version control extended now beyond “backups” and “portfolio” to “collaboration”. You see, one part of version control is “branching”. Or whatever you want to call it in SVN but in Git, it’s clear. A branch is your copy of the code. Merging branches means that you can have several people working on a project at once without having to “check out” something, wait for them to finish, and go back to work.
The consequences of that short trip in collaboration did not come into fruition until yet another year later, 2013.
In ’13, something wonderful happened to me. Version control grew beyond its basic usages to a way of life.
Version control ended up as a tool of abstracting EVERYTHING.
But let’s backtrack and look at some of the tools that hit mainstream last year. We’ve seen Vagrant become widely adopted, Docker, and tools like Ansible to tie server and local environment together. We’ve seen Bower and other package managers become commonplace and EXPECTED in projects. Then there was Grunt which allowed us to automate tasks.
These projects allowed something very amazing to happen. They extended version control.
So, let me get back to my story. I started working with Git on an every day basis. The collaboration part became essential as I grew branches, switched between features and projects, merged stuff down, and worked among peers that were in that workflow already.
Commit history started making more sense as commits became markers of more granular advancements. Commits came to mean ‘part of the logic for a feature’ rather than a huge milestone. So it became that even my own projects had a history. Doing git blame ended up revealing why code was implemented in the first place. So bam, let’s review. Version control is for:
- history keeping
My understanding and love for the subject deepened as branches ‘cross forks were used. Tags became releases and so we now add “scheduling” to our list. The ‘portfolio’ part grew as I got merge requests on my ‘showcase’ projects and people started to help. The term ‘open source’, in my mind, became synonymous with ‘collaboration’ and ‘version control’.
But what else can there be? Well, let me come back to those tools I mentioned, namely Vagrant, Ansible, Grunt, and Bower. On the surface, these tools are very different, but when it comes to version control, they are identical. They are all tools that allow us to describe our workflow, environment, and everything about our project in text files.
Vagrant helps us create virtual machines that we can work on, sure, but it also becomes a hardware spec sheet that we can iterate on. It becomes a local environment that we can virtualize and version. I do most of my work on Ubuntu, so guess what? My vagrant files feature 64-bit ubuntu boxes.
Ansible, Puppet, and everything else puts the base software on our virtual metal. Here we keep history of how our project works on our server. It requires PHP extensions, it requires certain databases, user names, passwords, even a process of starting up. Now, we can version how our ‘hardware’ works.
Next one to take up is Bower and package managers in general. This little tool helps us keep track of what version of what software we need to have to run the project. It also highlights how important version control is. People depend on ~1.x version of this and that. As someone who provides open source packages to be used in other projects, you need to make sure you keep your history clean, your tags working and just be mindful of how you record your history. So now, we version the depedencies.
Lastly, I have Grunt on the list. Grunt is a peculiar tool in that it’s a tool that just runs other tools and tasks. This final tool gives us clues as to how we work on projects. We’ve so far versioned our environment fully to make it run, our dependencies, our code itself, and we can launch it. Grunt tells us how to work on the project itself.
Note: Grunt requires Node and NPM to run in order to install grunt modules. The dependencies are listed in package.json.
It’s quite a revolution in thinking when you realize that you can version the way you THINK and work. The gruntfile and the command grunt tells us everything about how someone works on a project. It tells you that the folder dist uses the current distribution. That only the folder sass needs to be compiled with sass task, and that the compiled result should be in test before it makes it to dist. Grunt tells us that the express server should be restarted whenever we make a change in the Node back-end folder but doesn’t need to be if we’re working on the front-end. On the contrary, the site should live-reload when making changes on the client side.
Let’s zoom out a bit. We’ve documented, recorded, and exposed EVERYTHING that has to do with our code. From hardware, to software, to tasks to run, to dependencies. Now…we can package it up in yeoman and let others work on their projects the same way.
Phew. Crazy, right? And I haven’t even touched deployments! Deployments are another neat way of working with code because the idea is basically to define [yet another version controlled textfile] which runs certain tasks that copy a specific tag or version of your code onto a server, does some other things, and switches to the new (or old) version of code.
Okay, one more zoom out. Just walk far back, think about this article. Think about the way code is approached now. It’s fully documented, it’s fully versioned, history-controlled, fully dependent on all these text files and tools that help us recreate what others have done on a project.
There’s something interesting in this ‘version control’ concept that caught my attention. In the end, all of these tools allow us to disregard the machines themselves that we run. We have VMs to take care of any issues. These tools help us forget about which version of jQuery we use or need to use, it’s documented. We can forget about typing in sass –watch or whatever the command is. We can even forget about the passwords to our servers. We can forget just about everything and we’re free to test and work with our code without the fear of a reprise.
Now, we get to actual focus on the code logic.
Logic beyond language
When I first started programming, I was haunted by the idea of having to remember a ton of code syntax, the flow of programming, and APIs to libraries. While not a programming language, HTML gave me the chills with its numerous tags, unforgiving properties, and cross-browser problems. How was I to even approach any of this? I just wanted to make something.
When I started working in PHP, I found it pretty easy to remember most of the syntax, and very important to have documentation on hand. I’m still the same way. I’m sure I don’t know 90% of the language to the letter.
Hell, I spent some time with C# on an ASP project, jumped onto Unity3D and wrote a user controller. All of it seemed the same (to some extent).
Logic is what drives the code and it’s, in my opinion, one of the most important parts. It’s a question of “how can I get this done? And how can I get this done efficiently?” and the code comes after that.
About two years ago when I started building a major project, I found myself ignoring the language, because it’s really not that important. What was important was figuring out the “how” and my scraps of paper, notebooks, and even just bulleted points helped me with that. The Logic became the biggest asset of a project while the language became a tool.
This proved itself even more true last year as I tackled bigger and bigger projects.
The climax of my versioning catharsis was the realization that the more you do away with languages, hardware tweaking, and specifics, the better you’re off. When I started digging into the “how” in respect to solving a problem, I started to see past the languages.
One of the biggest game changers was when I saw one idea at my work implemented in five different ways. There was an original API built on top of Symfony. I prototyped out a more modern version with Laravel. My co-worker did yet another prototype via NodeJS with different front-ends. I did a KnockoutJS version, he did ReactJS and AngularJS. In the end, the important part was the “how”. It didn’t matter if the back-end was PHP, or Node. If it was Laravel or Symfony. If the front-end was React, Angular, or Knockout. All that was mattered was the “how” and everything else followed. The “logic” was pretty much the same except for the nuances of the languages. NodeJS being more callback-based and served more as an API engine while Laravel had a clean MVC structure.
There was yet another pivot point last year when I ported over an API from a sluggish Rails project (not sure if it’s Ruby’s fault) to a light-weight Node project that was then redone by someone else.
Getting my head away from the ‘this vs that’ war, being able to ignore the bare metal my software runs on, being able to set & forget my dependencies, being able to safely experiment, lead me to focus on the nuances of how the software is done and what it solves. It lead to whiteboarding. It lead to brainstorming and solving bigger issues than “how do I set this up on my laptop again?”.
Imagine “Capturing Logic”
I was thinking about “flow” programming or whatever it’s called and realized that no, that’s not how I want to program. I don’t like it, I don’t like not seeing what’s behind the nice UI. It’s the same as working on a PC, on a Windows stack which prefers heavy IDEs vs. working with Sublime Text and relying on the command line to do the heavy lifting.
I’m the latter type of guy. Flow programming however does lead me to believe that there has to be something done, something new.
We’ve abstracted out software requirements and either put it down as a dependency or packaged it away in some container or definition. We’ve documented how we work. And then we’re left with a bunch of code.
This is where the problem starts. This is where the revolution in (my) thinking starts. Maybe some of you have already realized this and I wouldn’t be surprised because, well, that should be normal.
My big idea for this year is Capturing Logic.
When I first start working on an already existing project, I have to go through it, diagram it out on a piece of paper. Figure out how the pieces fit together, figure out the paradigm.
Some projects work as glorified static pages with tons of helper classes stowed away somewhere. Others go through an intricate process of listeners, subscribers, and services. Yet others are simple MVC modules. And last ones provide a database API feed with a heavy front-end to digest it.
So how do you capture logic? Let’s define “logic” first:
“A particular method of reasoning or argumentation”
Basically, logic refers to the brains of an application that decides where information will go and what will happen to it.
The awesome thing with “Capturing Logic” is that we already have a systematic list of patterns that define logic: the DESIGN patterns. Whether you’re employing composition or the singleton pattern, it’s very obvious that most applications will fit into these patterns (and antipatterns) very quickly.
Diagramming applications doesn’t make sense anymore. One quick look and the paradigm is clear, and with that follows understanding of the flow of the project.
Also, one thing that made me stop in my tracks last year was a redditor (or member of HN) that wrote (and I paraphrase):
The programming problems that developers on the internet face today, we solved more than two decades ago when we built first consumer-based software. We solved them a decade prior to that with the first OS.
The comment made a huge impact on me because it made me realize that our entire strife to bring web development to the next level suffers from not learning from the history of our “programming forefathers”.
Logic stays the same, it just gets reapplied and get slightly modified to fit a new box.
Errr..didn’t you say “logic is a dependency”?
Yes, so I got a little bit sidetracked. Here’s the thing. Once you learn Design Patterns, it’s easy to see it as yet another form of “dependency”.
Include this “dependency” in our code, set up your application, apply the correct paradigm you want to follow and finally, you’ll have moist soil for the seed of logic to plant.
At which point, your logic will ultimately boil down to “if this then that”. And this logic is transportable across languages, systems, and dependencies.
Capturing logic is an easy matter. Once you know the patterns, you’ll know the inner-workings of most projects. You can abstract your coding to plug into these patterns quite easily and all you’ll be left with is the code that does the actual work without “boilerplate”.
Think about this another way. Imagine you have a garden. The tools you bring with you are dependencies, the process of planting the seeds is already written down, and now on top of that, you’ve just learned the most efficient gardening positions.
Not only do you know not to put potatoes next to onions (for whatever reason) but you’ve also learned that potatoes trive best when planted with 5cm spaces, and onions require full 30cm in between. This is the design pattern, it’s how things work very efficiently. All you have to focus on now is the watering, weeding, and putting together the right meddley of potatoes and onions. The “logic” in this analogy is just the decision of what to plant. Do you want more onions or potatoes? Should you add carrots?
There was another article that I read recently called Don’t call yourself a programmer that basically equated most programming to creating CRUD applications.
In a way, I’m saying something a little different. I’m saying that most applications boil down to “if/else” loops with a mess of code surrounding it.
How can I apply this in real life?
Look back at what I’ve written. The best way to apply all this knowledge is to, first of all, version as much as possible but only to the point where it makes sense. It doesn’t make sense to spend 5 hours writing the perfect Grunt script.
Treat these things as “boilerplate”. Setting up an application should be quick. It shouldn’t take 10 days just to get something working. It should take 5 hours to be done.