Refactoring existing code makes better software. Martin Fowler has championed this concept and as a result, developers look at refactoring code as an essential part of the process — not something to do on slow days. Of course, idealizing over code design is one thing — making it a reality is another. Unfortunately, refactoring doesn't happen for one reason or another — usually there isn't enough time.
But what about the programmers that have to stare this code in the face — day in and day out, watching unruliness assume control? They're the ones who see the problematic consequences of letting ugly code grow and metastasize into other subsystems. Programmers probably don't decide there is no time to for refactoring.
If there is no time to refactor code, when will it get done, if ever? Understandably, features and enhancements are expected. Expected yesterday. Maybe there is a way for software developers to convince stakeholders that refactoring is the only option left. Maybe we can treat, somehow, refactored code as a feature?
Code gains weight fast
Code gains weight at an alarming rate. You might start off your hacking session with a small set of classes, maybe a couple utility functions. It takes only a couple hours for this lightweight setup to transform itself into a bloated plate of spaghetti code. Not because you don't know how to write elegant code, but because others are depending on you — it just needs to work. If we wanted beautiful code the first time around, we're in the wrong business. Refactoring is about improving existing code, not code that has yet to be written.
As programmers, we idealize solutions — before sitting down and typing it into the editor, we've at least, if only briefly, conceptualized a few alternative approaches to tackling the problem. This isn't considered big, upfront design — it's more of a personal game plan to help spring us into action. But, since this is only a quick mental note — not using real code and a real editor and not running real tests — chances are the some aspect of our plan is wrong.
The trouble is, we don't know that some aspect of our ideal implementation is invalid until it's concretely implemented and executed. Sometimes our code doesn't even reach the point of execution before we've realized it doesn't fit. Sometimes all it takes is a glance at the interfaces you're working with or the documentation of another component dependency to realize that there is a sudden change in plan.
This sudden realization means that the nice, natural flow that we had envisioned, the one that motivated us to get going in the first place, has been scrapped, ups the urgency of the situation. Maybe the entire metal workflow isn't thrown out, but the calm confidence we had slowly dissipates. We're probably not following a waterfall approach — so we need to adapt quickly — pound out something that'll work. Otherwise, the forward motion of the entire project is jeopardized.
Fixes to sudden disruptions aren't difficult to implement, but they're not the most elegant or efficient solutions. And so the code gains weight. It works, but it's become heavier — in terms of raw size and in logical baggage — all within a few hours.
Features are released on time
Software projects have a brigade of developers, all with the attitude of making sure things get shipped when expected. Even if that means not setting aside some refactoring time. This process, this seemingly endless cycle of pounding out lines of code, is very rewarding. Getting stuff done on time is a recipe for success in any software project as far as I'm concerned.
This is the habitual part of it for the programmers — who wouldn't want repeated success by continuously pleasing customers and other stakeholders by shipping early? Don't fix what isn't broken, right?
And so life goes one — each cycle of the development process yielding a new and enhanced version of the software that exceeds customer expectations. It's hard to change a good thing while it's good — making decisions to refactor code can seem unjustified. Is there another way to squeeze in some code improvements that don't necessarily please the client directly?
Well, small incremental improvements can typically be tucked-into sections of the software system during the development process. These are low-risk, low-effort improvements that don't have an immediate impact on code comprehensibility but instead have a concerted long-term impact. The trick is — making these small incremental refactorings — is really only beneficial if they're a habitual task of each and every programmer working on the project — cumulative change for the better. But this can be overridden, mentally, by that other little habit — satisfying the customer by shipping on time.
Forcing the issue
Two competing ideologies vetted against one another — the ship on time versus the refactor messy code — lead to one of two places. Perhaps the worst thing that can happen is programmers get caught up in cleaning up code and as a result, deadlines for requested features tend to slip. What we have in this scenario is some nice looking code and a frustrated group of stakeholders. The product is better off now that the mess has been tidied — paving the way for future ingenuity — but how do you explain that to non-programmers?
The second outcome, and perhaps a good selling point for investing time in refactoring code, is a garbled mess that can't be updated with new features or enhancements to existing features. There is an illusion that this will never happen because it takes a while to happen. A misconception of those involved in software projects who don't write any code is that code is an immutable thing — once it's written, it's there and it should just work. Ask any software developer and they'll tell you that any body of code is a living organism — one that must be fed and cared for.
So, forcing the need to refactor. The best way to explain it to those involved in the project but do not understand the concept of refactoring code is to present it as a feature. Or, sometimes even more effective, present it as a blocker for new, potentially interesting features. This second approach takes some inventiveness, but is ultimately the most effective way to communicate the state of the code base to non programmers. Think features they'd like to see and what would stop them from happening in terms of the current state of the code.
The reason this approach — let's call it proactive refactoring for features — works so well as a communication tool is that you're taking a deficiency in the fundamental code structure and presenting it in business objective. Rather than trying to introduce the philosophy of refactoring code — because I can tell you right now they're not interested — you're taking a valid business concern and presenting it in a cohesive way.
One final suggestion on proactive refactoring for features — back your claims up with evidence. Not a written words, but a quick little prototype that'll make very obvious the problem at hand. Nothing says urgency like proof that a potential money making feature might not work in our product down the road.
Showing posts with label proactive. Show all posts
Showing posts with label proactive. Show all posts
Monday, October 3, 2011
Tuesday, February 24, 2009
Optimistic provisioning in the cloud
One of the technological problems that cloud computing technologies are supposed to solve is the lack of computing power when it is needed. Computing on demand, so to speak. The elasticity of the cloud enables this.
The classic example of this is when a web site operating in the cloud gets "slashdotted" and does not have the necessary computing resources required to fulfil the requests, your site dies and readers (soon to be ex-readers) will be disappointed. Luckily, your site is running in a cloud environment and has the ability to "expand" its' computing when the demand requires it.
What happens when the actual expansion takes place? Generally, a new virtual machine is created and that machines' resources are now available to the process that requires it. The process in this context refers to the overall business requirement that caused the expansion event in the first place. The process that says "give me more computing power" may in fact result from a general discussion amongst several nodes in the cloud.
Here, we have a simple controlling process that handles requests. These may be client requests or requests from other nodes in the same cloud. The controlling process then forwards the request to a resource management process. It is the responsibility of the resource manager to ensure that computing resources are available to fulfil every request. This is where the bottleneck lies, in has_resources(). In the most common case, there are plenty of resources available and has_resources() has very little work to do. However, when resources start to dry up, it needs to make more resources. This is where the costly work of the resource manager lives. It would be great if there were some way to know ahead of time what the peak resource demand will be.
Unfortunately, there is really no reliable way to do this. The best we can do in this situation is guesswork. The resource manager could monitor the distance between the size of resource requirements in a given time interval. Certain thresholds could then be set and once reached, we could then provide resources based on what the probable resource demand will be in the near future.
For instance, lets say I have a simple running within a cloud environment. I post a new entry, "a ton of traffic". Now, before I post this entry, I have an average demand of 5 requests per hour. An hour after posting, the resource manager notices that my average has doubled to 10 requests per hour. This is something that could be handled very comfortably be my service. However, the suddenness of this relatively large change could put the resource manager on alert. Now, hour two after posting "a ton of traffic", the number of requests reaches 20 requests per hour. It seems that this raising demand trend is continuing. The resource manager would then proceed to making more resources available.
With this approach, there is always the risk of over-provisioning resources. This type of data can be misleading. However, it does lend a guiding light toward proactive provisioning. Besides, if the statistical data is misleading, it is better to cleanup over-provisioned resources than being trying to do a huge provision job during the high resource demand.
The classic example of this is when a web site operating in the cloud gets "slashdotted" and does not have the necessary computing resources required to fulfil the requests, your site dies and readers (soon to be ex-readers) will be disappointed. Luckily, your site is running in a cloud environment and has the ability to "expand" its' computing when the demand requires it.
What happens when the actual expansion takes place? Generally, a new virtual machine is created and that machines' resources are now available to the process that requires it. The process in this context refers to the overall business requirement that caused the expansion event in the first place. The process that says "give me more computing power" may in fact result from a general discussion amongst several nodes in the cloud.
Here, we have a simple controlling process that handles requests. These may be client requests or requests from other nodes in the same cloud. The controlling process then forwards the request to a resource management process. It is the responsibility of the resource manager to ensure that computing resources are available to fulfil every request. This is where the bottleneck lies, in has_resources(). In the most common case, there are plenty of resources available and has_resources() has very little work to do. However, when resources start to dry up, it needs to make more resources. This is where the costly work of the resource manager lives. It would be great if there were some way to know ahead of time what the peak resource demand will be.
Unfortunately, there is really no reliable way to do this. The best we can do in this situation is guesswork. The resource manager could monitor the distance between the size of resource requirements in a given time interval. Certain thresholds could then be set and once reached, we could then provide resources based on what the probable resource demand will be in the near future.
For instance, lets say I have a simple running within a cloud environment. I post a new entry, "a ton of traffic". Now, before I post this entry, I have an average demand of 5 requests per hour. An hour after posting, the resource manager notices that my average has doubled to 10 requests per hour. This is something that could be handled very comfortably be my service. However, the suddenness of this relatively large change could put the resource manager on alert. Now, hour two after posting "a ton of traffic", the number of requests reaches 20 requests per hour. It seems that this raising demand trend is continuing. The resource manager would then proceed to making more resources available.
With this approach, there is always the risk of over-provisioning resources. This type of data can be misleading. However, it does lend a guiding light toward proactive provisioning. Besides, if the statistical data is misleading, it is better to cleanup over-provisioned resources than being trying to do a huge provision job during the high resource demand.
Subscribe to:
Posts
(
Atom
)