We’ve all been there before. You can envision the final product of your labour, but you don’t quite know how to get started. Not only that, but you’re cautious about getting started without knowing what solution to go with because you don’t want to “waste your time”. This is a classic problem for developers, but there is a way that we can ease this awkwardness by jumping right into our work.
Disposable code is any code written with the intent of it being easily tossed out. This doesn’t mean that the code is bad code necessarily, but more so that it is designed to be short-lived or easily replaced with other work.
This begs the question of why we should use disposable code when we can simply get it right the first time. Suffice to say, it’s never easy to know what is the “right” solution to a problem, even if you are an experienced developer. You will want to run your own experiments to test whether various methods return what you want in order to explore the space you are in. It’s a natural human curiosity. Rather than sitting back and mulling it over, sometimes it is much easier to get into the meat of the problem and uncover any hidden work that you hadn’t thought of before. Remember: there’s no danger once you have accepted that you will simply throw this code away once you are done.
A perfect use case of disposable code is for proofs of concept (POCs). The purpose of a POC is to show that something can be built and how it may be built. The code that you write for a POC is not meant to be kept for the actual implementation of the feature, but it can be referenced. In this sense, you know that you are writing something that will never see the light of day, so you feel much better taking some shortcuts and solving the real crux of the feature. The sky is the limit on what you can do here. The most important part of this experiment is to explain to your stakeholders that this was just that—an experiment. You do not want to give the illusion that this is a finished feature to anyone because it is simply disposable code.
As said before, one of your first steps here is to accept that this code will most likely be tossed away. Once you have accepted this, we can begin our process.
The first step is to identify our goal and get writing. Regardless of how pretty our work is, the goal is to see what we want to see on our screen. If you need to write a nested for loop, do it. If you need to do data transformation on-the-fly, do it.
# Let's find the minimum price for a Spree::LineItem across all orders from today def todays_min_price todays_orders = Spree::Order.where( created_at: Time.now.beginning_of_day..Time.now.end_of_day ) min_line_item = nil todays_orders.each do |order| order.line_items.each do |line_item| if min_line_item.nil? min_line_item = line_item elsif line_item.price < min_line_item.price min_line_item = line_item end end end min_line_item end
We have no time for preconceived notions of beauty when writing this code. We are trying to break ground, not pass a code review.
Once we have shown ourselves that we are capable of doing what we set out to do, we now enter our second step. This is where we can begin to pick apart our code and question why we did things in certain ways. This is the perfect time to now mark that nested for loop as an area of improvement in our next step.
# This isn't the greatest. Surely there must be an cleaner way... min_line_item = nil todays_orders.each do |order| order.line_items.each do |line_item| if min_line_item.nil? min_line_item = line_item elsif line_item.price < min_line_item.price min_line_item = line_item end end end
The third step is to iterate on our code based on the flaws we found in our second step. If our goal is to eventually get this through a code review, then this is the time for us to simplify or abstract our work. Things that you were doing directly in a controller can now be abstracted into models that are encapsulated with proper specs. Whatever you believe should be done to refine your code should be done here. I have found that it is much easier to try to fix one flaw at a time rather than fix the entire thing all at once.
# Looking better at least! # And you can be make it even better with iterations. minimum_line_items = todays_orders.map do |order| order.line_items.order(:price).first end minimum_line_items.min do |a, b| a.price <=> b.price end
This process is a bit of an antithesis to test-driven development (TDD), but I think they both exist in their own space. They are but two choices on the menu that we have as developers.
This is a bit of a contrived example, but it could have realistic potential in reporting. You can take it as far as you’d like!
def todays_min_price minimum_price_for_range( start_date: Time.now.beginning_of_day, end_date: Time.now.end_of_day ) end def minimum_price_for_range(start_date:, end_date:) orders_for_range = Spree::Order.where( created_at: start_date..end_date ) minimum_line_items = orders_for_range.map do |order| order.line_items.order(:price).first end minimum_line_items.min do |a, b| a.price <=> b.price end end
There always needs to be a balance between disposable code and the long-term sustainability of the project. Sometimes you may find yourself in the unfortunate situation of needing to merge this disposable code because of a time crunch or other factors. Obviously this is not ideal, but I understand the pressures. If you are going to do this, the least you can do is leave this code well documented with comments of what should be done, and then this should be addressed as soon as possible. The only thing worse than leaving disposable code in the codebase is to now treat it like permanent code. All code is disposable.
If you are hoping to benefit the long-term sustainability of the project, this code should now be abstracted into models and adhere to SOLID principles.
If you did not merge this disposable code, then it can now be used to inform the future sustainability of the project. When writing the actual implementation, you can look back at the disposable code (or POC) and use it to see how to best develop the feature (or at least, how not to write the feature). Sometimes writing disposable code will even tell you that this feature doesn’t need to be written at all. This is the best-case scenario for long term sustainability because we get to reuse something that has already been written. Just remember to make sure to improve that section of code if need be.
When used properly, disposable code is a very powerful idea and mindset. If you don’t intend to keep any of the code that you write in this session, then it doesn’t matter what you write. This is an intentionally freeing feeling that keeps you from worrying too much about production-ready code. It allows you to dig into the bulk of the problem at hand rather than prematurely optimizing everything. Don’t get me wrong, there are times and places for this methodology, but it’s an important tool to have in your belt as a developer. When you have a plethora of tools to use, it becomes easier to realize which tool would be best for the job. If you have made it this far, I hope I have convinced you to write some code to throw away.