me and two other developers mobbed on a programming task. During that they asked the following questions:
- I feel like I spent too much time on the prior approach, how would I know to take a different approach?
- We’ve added a few different generators that will write code into the target application. Yet what we’re doing in this ticket is to not generate code. Should we look at the others and adjust to this alternate approach?
We talked about these a bit, and I left the meeting thankful to work with team members always asking questions and seeking to understand.
Spending Too Much Time
When the first developer asked how to avoid spending time on an undesirable path, my first response was summed up in one word: Experience.
What I now actually said was along the lines of the following: “I’ve been programming in Ruby on Rails 📖 for 15+ years. So to know the path to not take, have years of experience. Also, I have been working in this nuanced stack for quite some time.”
Now, as I reflect, I’ll add this additional context.
In this software space we have two topographical concepts: layers and tunnels. The layers build on each other and the tunnels cut across the different layers. Creating a muddy ball of twigs. Or spaghetti and meat balls if you prefer a food analogy.
With that as background, I want to dig into some potential strategies.
What Does Your Testing Process Say?
One strategy is to pay attention to the testing process. How are you testing these changes? What all goes into executing your testing script (be it automated or manual)?
What would need to be true to make the testing easier? How can you isolate the changes you’re making to narrow the surface area of the testing?
Also consider that there are multiple layers of testing. You can work in “isolation.” Write and run those tests. Then as you expand your scope think about how you’ll test integrations.
What Do You Control and What do You “Unleash”?
Another strategy is to look at the “control” you have over the change that you’re working on. Think about what happens once you release this change, how easy will it be to adjust that change? And how will we you be able to propagate those changes to those that adopted your code?
In the strategy of control you’re thinking about boundaries. The larger the surface area you expose the more you need to guard and control. What could you be doing to minimize that surface area?
One strategy is to make very narrow public methods. For example, when I write a Ruby 📖 gem, I like to create methods on the module of the gem’s namesake. Those are the only public methods. Everything else I strive to keep isolated; which is difficult when you have a twiggy ball of mud.
But again, the principles are in play.
Ask for Someone to Join You
If you are getting any inkling that “this should be easier” or “this doesn’t feel quite right”, hone in on that voice. Go find someone to pair with you. Explain the situation to them. Restate the specific problem you’re trying to solve.
Show them the diff of the changes you’ve made from the main line. Maybe they’ll see a different approach.
And let me be clear, folks would rather be “pestered” to jump in early and help out than to show up towards the end of the work and play a game of retrofitting and damage control.
Also, while seeking help, ask questions. Get the person to think out loud.
Reviewing Alternate Approaches
The second topic we brushed on was around when to take one approach versus another. And the aforementioned strategies apply; but I want to delve deeper.
One of the common sources of bugs in software development is unexpected mutation of state. A classic example is when a method has a side-effect; quietly changing an instance variable that is used after the method call. Functional programming aspires to reduce/eliminate unexpected mutation of state.
In a broad sense, this also applies to how we can make the decision. In choosing a solution path, how might the downstream state unexpectedly change? When we “generate” code, that code is no longer ours to control; much like our children.
I love my children, but they are also can be a source of heartburn. And I have so very little control over what they do. Yet, when they were younger, I was liable for their actions. When you generate code into someone else’s application, it can create uncertainty as to who “controls” or “should maintain” that code.
Yes, it’s in the application’s repository, but someone else put it there.
In Ruby on Rails, initializers are a great thing to generate. Expose the configuration points. But the further you drift from configuration and into “here’s fully formed methods” the more likely you’re setting yourself up for heartburn.
Instead, look at how you might be able to make changes within your library/gem that will propagate (without code generation) into the downstream application. Maintain control of that, so that when bugs and changes emerge, you can more readily fix them and see them again propagate downstream.
My primary professional language is Ruby. I love it. It provides amazing tools for developers to bend, twist, and break things that most other languages simply just don’t expose. In other words, there’s tremendous power. And to quote Uncle Ben (of Spiderman) “With great power, comes great responsibility.”
Now understanding when and how to use those powers comes from experience and I assume introspection on said experience.
In , I joined Scientist.com, in part to help grow the team of developers, in particular the Software Services by Scientist.com 📖. And each day I leave work smiling. I am working with such a fantastic group of folks; a euphoric mix of curious, capable, and compassionate people.
Each morning, I wake up eager to get started on the programming tasks assigned to me, but also hoping that folks will reach out and pull me into helping solve the tasks assigned to them. I feel a tremendous sense of teamwork each and every day.