In a typical software life cycle, most projects begin as a bouncing baby prototype and later develop into a strong, trusted production application. At these two distinct stages of software development, the priorities, pace, and coding practices can differ greatly. Understanding these nuances and properly planning your prototype to transition into a product can save you a lot of time and money. And by protecting your bottom line, it can increase your chances of project success.
Prototype development is defined by a rapid development pace for iterating and testing. Oftentimes a prototype is created as a simplified version of the desired final product or feature that is used to test concepts before fully committing to them. Prototypes are a great way to get stakeholders on board with a new project or changes to an existing project without making costly upfront investments. Early prototypes can be simple sketches and drawings, but in this article I'm going to focus specifically on coded prototypes.
There are three main priorities during the prototype development phase: the ability to rapidly iterate, receive quick feedback, and keep costs relatively low.
Prototypes are generally created during times of uncertainty, when we have questions like: Is this a viable product? How easy will this be to implement? Will end users like this? We want to be able to answer these questions quickly and rapidly iterate until we find the best solution or get the answers we're looking for.
Rapid development usually involves a more bare-bones approach, which may include coding a lower fidelity version of the desired feature, and usually involves trimming the bells and whistles you might find in a production app, like complicated testing frameworks, expensive database management, etc. To help with efficiency, appropriate technical designing and planning should be done before beginning to code.
Quick feedback is equally important. The primary goal of rapid iteration is the ability to test and evaluate the iterations. Oftentimes during the prototype phase we are going back and forth between stakeholders, presenting new ideas and features, and interrogating their viability. It's important that decisions about the viability of a prototype or iteration are communicated in a timely manner so it can either be efficiently adopted to production or scrapped and sent back to the drawing board. A slow moving evaluation or feedback cycle diminishes the purpose of being able to develop rapidly, and it wastes a lot of time.
Expediting the feedback cycle can be achieved through having well-defined metrics for evaluation. You need to know what your pass/fail conditions are, who will be doing the evaluating, and what the deadlines and expectations are for the evaluators. For example: I need the VP of engineering to approve the login redesign by Wednesday afternoon. If these are not well-defined, your prototype might be waiting in limbo, unable to make progress with no clear path forward.
Keeping costs low is a significant priority during the prototyping phase. You want to be able to get the answers and feedback you need without expending a lot of time and money. Since you are prototyping in a time of uncertainty, anything you are working on could be scrapped and sent back to the drawing board. Investing too much time and energy developing any aspect of a prototype could be wasteful. And if you become too emotionally invested in a prototype, it can be disheartening when things don't go as planned.
Important Considerations During Prototype Phase
Though there is an emphasis on speed while developing a prototype, it's important to remember that your code could be the foundation for a future production application or feature. For this reason, it's important to recognize the distinction between prototyping and "hacking." Hacking (or coding with an extreme emphasis on quick and often unstable, temporary solutions) can be great for learning, creating a short-lived proof of concept, or just having fun. However, prototyping is often not a good time for hacking.
Be aware of the technical debt you are creating when coding a prototype. Technical debt refers to the cost of choosing a simpler or faster solution in the short term over a better approach that would take longer to implement. You pay off your technical debt by eventually refactoring the code to the preferred solution. And, to continue with the debt metaphor, you often pay interest on the technical debt that you choose not to pay off in the form of having to constantly work around the undesirable code.
Some amount of technical debt is unavoidable, especially in the prototyping phase. Because your main priorities involve speed and keeping costs low, less than perfect code is going to exist within the prototype. The most important thing is keeping track of where the technical debt exists within the code. What areas need to be refactored for performance before moving to production? Is there a chunk of untested code? Documenting these areas, either through
// TODO: ... comments within your code or keeping a backlog of technical debt tickets in a project management tool, will help you prioritize paying off existing technical debt and also help prevent the debt from getting out of hand.
Balance is essential. When prototyping, you want to follow the Goldilocks Principle: find the happy medium between “quick and dirty” prototype code and slow, solid production code. Every project is different, so depending on your budget, timeline, and project scope the degree of speed versus rigor can vary greatly. If you find that your prototype is becoming too buggy or it's hard to make changes in certain areas, it might be time to slow down and harden what you already have. Conversely, if you're constantly struggling to meet iteration deadlines, it might be time to evaluate your process and simplify your code to improve your pace.
Prototyping is really just a phase. Another important thing to keep in mind when prototyping is knowing when it’s time to move out of prototype phase. This can take one of two forms: The prototype is given the stamp of approval to graduate on to production phase, or the prototype gets a thumbs down and fades quietly into a git history. The happier of the two scenarios usually occurs when important stakeholders are pleased with the prototype, you've gained all the answers you were seeking about the viability of the project, and you feel confident in investing in the long-term future of the project.
While it might be sad to contemplate our beloved prototype getting the axe and never fully developing into a big, strong production app, that's simply part of the process. This is why we prototype. If we don't find the proof we are looking for to validate our ideas, it's best to let them go and move on to the next idea. The concept of "failing fast" is prevalent in software, systems design, and business and it's certainly relevant here. Being able to recognize when your prototype is headed in the wrong direction allows you to either reevaluate where you want to go with the current project or head back to the drawing board for a new prototype. If you kept your costs low, as recommended above, this should soften the blow of having to say goodnight to your current prototype.
Assuming everything went well with your prototype - you got the proof you needed to move forward and fully invest in the project - it's time to shift gears and start production development. Like prototyping, production has its own set of unique priorities and practices.
A primary priority for a production application is creating a consistent experience for end users. Production applications are generally used by the public or a wide audience of customers where the reputation of the product and its owner is on the line. Users typically demand flawless, fast experiences especially when alternatives exist on the market. Even small bugs or minor inconsistencies caused by variable screen sizes or internet speeds can become major problems in a production application.
Consider all end users. What devices will they be using? In what environments and settings will the app be used? Is your app accessible for users with visual or hearing impairment? Having a careful consideration for both the general and edge cases will help you harden the application for all users and avoid releasing a potentially embarrassing bug or leaving a user with a sub-par experience.
Similarly, performance under stress is a priority for production applications. Your prototype may have worked seamlessly for 6 months of testing, but what happens when 1 million users install and start using your app on launch day (the dream)? While building a production application, you have to consider not only the expected cases but also the unexpected. Making sure your application is scalable and performant under extreme scenarios is a priority during production.
To help achieve the goals above, the behind-the-scenes priority of a production application is solid, accurate, well-tested code. Your code must be trustworthy, meaning that you can rely on it to produce the expected behavior 100% of the time. Your work in progress code might have its bugs, but what gets released to production should be solid. Writing tests and performing manual testing of the app help confirm the trustworthiness of your code. While your code needs to be solid, it is still a growing, changing organism and therefore must be buildable both to increase scale and to add new features. Clean, dry, well-documented, and organized code is the best foundation for build-ability.
Production Code Practices and Considerations
Though the prototyping has its own structure and set of standards, it usually feels like the wild west compared to the rigor and standards of production development. A cornerstone practice of production development is strict code review with high standards. Depending on the project scope and the size of the team, the standards and amount of code review may vary, but every production focused team should have a well-defined code standards and review process.
Examples of code standards might include: each utility function must have a corresponding unit test for all possible edge cases, all code must be adequately documented, code must pass review by two team leads before merging into production code, and/or adequate manual regression testing must be performed before merging code.
Quality Assurance (QA) testing is a common practice for production development. QA is the maintenance of the optimum level of quality to ensure that the product does not degrade or lose quality over time or as features change. Software QA testing, performed by testing professionals either within or outside of the development team, is a way of evaluating the production code regularly, assuring consistent performance and user experience.
Given the high level of code standards, review, and quality assurance, it goes without saying that production development has a significantly slower development cycle relative to prototype development. And that is an important fact to consider. Building and maintaining production code is costly, and the throttled speed (especially if you're transitioning from prototyping to production) can be frustrating for some developers.
However, while prototyping has a special place in my heart, production is where the money is at--literally. The slow and steady pace of production ensures the best end user experience, which is (typically) the ultimate goal of a project. Additionally, the rigorous testing and well-documented, trustworthy code can give you peace of mind as a developer when making a cross-cutting addition or upgrade to production code.
Avoiding Common Pitfalls with Prototyping & Production
A common pitfall during the prototype phase is sloppy prototyping--or hacking. If the technical debt is so high at the end of your prototype phase that you're better off scrapping what you have and doing a complete rewrite, you’ve just wasted hundreds of hours of developer time. But you wouldn't be the first! It's easy to get in the groove of rapid development, but remember that it comes at a premium.
Here are a few steps for preventing your prototype from getting too sloppy. First, write tests for vital pieces of code even in the prototype phase. If the chances are high that a particular piece of code will end up in production, it's best to avoid that technical debt and harden it now. Second, understand that the code you are writing is a developing organism that may (hopefully) eventually develop into production code. Love and nurture that code before it turns into a monster. And third, empathize with the future developers who might have to build upon the prototype you’re writing. Are you writing code you’re proud of or would want to build upon in the future?
Adhering to Production-Level Standards Too Early
Conversely, some developers are tempted to put a high level of investment in the prototype phase, adhering to high standards and taking the slow, steady approach. This is costly. As iterations become too expensive, you might get stuck with a bad design you can’t afford to change. If you start production-level developing before the project is properly vetted, the project might be scrapped before it ever reaches the end user. That would be a major sunk cost.
To avoid this, it's important to have an honest conversation with yourself and other stakeholders to determine if the product is ready for this type of investment. Have you done market research? Is the product funded? Are you truly confident about your designs, both visual and technical? If not, it might be a good time to reevaluate your approach, simplify your process, and get the proof you need before fully investing.
Maintaining the Prototype Standards After Releasing the Product Publicly
The tempo of prototyping is fast and fun. Many developers prefer the fast and (sometimes) loose standards of prototyping and are tempted to release products while their code still marches to the beat of a prototype. In a lot of cases, this can result in poor user experiences, embarrassing bugs, and damage to your product's reputation and overall success.
Make sure your product and all new features are properly tested and hardened for performance before a formal release to end users. Ask yourself questions like: Has my code been reviewed and manually tested by someone I trust? Am I 100% confident that my app is going to work as expected for all my users? Am I proud to release this application or new feature?
Shallow (or Non-Existent) Documentation
Lackluster documentation can be a problem in both the production and prototyping phase. Documentation is often overlooked during the prototyping phase, though it is vitally important. I have had the pleasure of seeing apps through from prototype to production, though in many cases, the prototyping team differs completely from production team. If the documentation isn’t available, it is considered technical debt, and in the worst cases, results in a considerable code rewrite.
The solution to this is simple: Be extra thorough when documenting prototype code. Clarify areas that will need work in the future with
// TODO: comments. At the very least, make sure the prototype has appropriate documentation before handing off to another team or pausing the project -- you don’t know when it will get picked up again.
Transitioning an App from Prototype to Production
It is a happy day when your little prototype gets the green light to grow up and become a real production app or feature. At this point, the work has only just begun. Here are some tips for helping you transition from prototype development to production.
Planning the Transition
Planning the transition should start early, during the beginning stages of prototype development. Early on, you should have your standards for production code defined and an understanding of how your standards are different from the prototype. How much code review is required before merging new features to production? How much documentation? What are testing standards?
Keep track of what prototype code needs retrofitting, where corners were cut, and your technical debt. From this, devise a plan for remedying these short comings when it is time for production. Plan to tackle the most critical issues, those shortcomings that may threaten the integrity of your production app, first.
Make Sure You Are Building New Features on Solid Foundation
A solid foundation is essential to creating a trustworthy production app. Resist the urge to build new features before going back and bringing prototype code up to production standards. Eventually, you’ll have to do the refactor to pay down your technical debt. It will be easier to do while the code base is smaller and the features are simpler.
While major technical debt should be addressed from the start, more benign yet unkosher aspects of the prototype may remain after transitioning to production. This is okay. However, as you add new features in these areas and notice the unsavory or untested code, take this opportunity to clean it up. This way you are leaving the files or blocks of code better than you originally found them, passively paying down technical debt.
Maintaining & Adding Production Code
So you've made a prototype, raised it into a grown-up production app, and released it into the wild. Now what?
Prototyping Features for Your Production Product
Even when your app has grown out of the prototype phase, your prototyping days are far from over. Chances are, your production app is still a growing, evolving organism. Users and stakeholders will demand new features; engineers will demand refactors and updates. Complicated changes and additions are best implemented first as prototypes. This way you can iterate and test before fully committing to writing production-quality code (which, as we've learned, comes with a premium).
For example: Let's say you desire to refactor your entire backend from a traditional REST to graphQL API. Instead of overhauling your entire service at once, section off and quarantine a single endpoint and refactor it alone with graphQL. This is your prototype. You will be able to see how the new service integrates with your ecosystem and if the performance payoff is worth the transition. You might find that it isn't, and then you've saved yourself a lot of time and effort. If you find that it is, you can be confident in your decision. Remember: at this stage, it's more important than ever to write the prototyped feature to be easily retrofitted for production.
Hone Your Production Development Process
I talk about production slowness in a relative sense, but production development doesn’t have to be slow. With the right tools and organization, rapid development in a production environment is entirely possible. I know, because I've experienced it.
One thing we use at Olio Apps to help increase our developer efficiency for both prototypes and production apps is TypeScript. TypeScript performs static analysis of code to help find errors before running the code, which shortens the process finding and fixing issues. When making cross-cutting refactors, TypeScript helps provide an additional layer of security against breaking changes as well as subtle bugs. Combining TypeScript with Visual Studio Code has significantly improved my developer efficiency by providing me with in-editor compiler warnings, code completion, and inline type-related tips. Finally, TypeScript is my preferred language for both prototypes and production apps because it forces you to think of your API up front, define types and interfaces, and encourages you to write more intentional code.
There are a lot of tools and technologies out there to help with your productivity and efficiency, too many to list. Sometimes you might find that creating your own tools to help with your very specific case is the best and most rewarding approach. One example of that at Olio Apps is Sitka. Sitka is a library for constructing strongly typed APIs for managing your Redux store while drastically reducing the typical redux boilerplate. If you are a Redux user, I strongly recommend checking out Sitka and seeing how it could help enhance your development speed. I also recommend interrogating your current development process in general, asking yourself how you could make it faster and more robust, and start using (or creating) a tool to make that a reality.
There's a lot to cover in the realm of prototypes and production apps. The whole software life cycle is a wheel of planning, prototyping, and producing. Each step of the way, it's important to keep in mind your priorities--is it time to move quickly to receive feedback and proof? Or is it time to slow down, harden, and develop for trustworthiness and permanence? I hope this article has helped you appreciate the difference between prototyping and production development, how to transition between the two, avoid hacking, and how to maintain growth after graduating to production. Now go forth and create software you're proud of, not a buggy little monster.