title: Breaking the Rules: Refining Prototypes Into Products author: Darren Bane copyright: 2020 Darren Bane, CC BY-SA # Abstract Recommendations for a process to refine prototypes into production-quality code are made. *TODO*: Q: re-cast much of this document as Architecture Decision Records? A: N # Introduction The conventional wisdom is that prototypes should be discarded once the lessons have been learned, and the final product written again from scratch. In the spirit of [1] I argue that improvements in development tools have invalidated this. *TODO*: case study # Previous Work There is a long history of recommending prototyping as a way to construct systems. I would personally recommend [2] and [3]. *NB*: I am almost certainly re-inventing a SmallTalk wheel. However I argue that Lisp's combination of imperative & OO is an easier sell to industry whereas pure OO as in SmallTalk (or logic programming as in Prolog) is still niche. A closely related are is that of "specification animation", quickly writing an implementation of some subset of a formal specification in for example Z or VDM. Prolog is a common choice for this, but I choose Lisp instead. However, as stated in the introduction, I differ in arguing that it is possible to *refine* a prototype into a product. # Prototyping The first step is to construct a prototype, or in modern terminology a "Minimal Viable Product". These recommendations follow on from [2] and [4]. The following is probably the most work that makes sense without earning money. ## Design Decisions The programming language chosen is a particular style of Common Lisp. For readability my [ISLisp-like subset of CL](bane.20.cdr15.md) should be followed where practical[5]. Reasons for this decision include: * Contrary to a lot of other languages, Lisp is fairly paradigm-agnostic. Imperative, object-oriented and (limited) functional programming is fairly natural. * The imperative and object-oriented paradigms are commonly taught, used in industry, and have a small "impedence mismatch" to current hardware. * The existence of quicklisp. Popularity is not really a reason for choosing Common Lisp over ISLisp, but slotting into quicklisp *is*. Detailed implementations, libraries, etc. are as follows: * The SBCL compiler. * Avoid multi-threading at this stage, event-driven should do the job. * Not sure if this is relevant for a prototype, but you could do simple multi-user with IRCv3, including the bots (nickserv, chanserv), and tilde.chat. For a very mathematical domain, APL might be a better choice. However, if more than the mathematical features are required it could be difficult to satisfy expectations. ### Dependencies For the prototyping phase, you should *really* limit yourself to the ISLisp subset. If absolutely necessary you can choose some of the libraries mentioned in the "Productisation" section below. ## Coding standards Even though this is a prototype, attention should be paid to basic craftsmanship. * Divide the system into packages, using the subset of CL that is supported by OpenLisp * Write docstrings for at least each public fun, class and package. There are good guidelines in the Elisp manual, but for now one sentence will suffice. * Use `declare` to check the types of parameters in public interfaces (see below). * Indent all the source code using Emacs. * Some minimal documentation, at least an overview like in [README driven development](https://tom.preston-werner.com/2010/08/23/readme-driven-development.html) and man (actually, [mdoc](https://manpages.bsd.lv/toc.html)) pages[7]. * Certain parts of a system justify greater detail for a *complete* specification. These are (newly-designed) network protocols and complex persistent data models. For new protocols, use JSON-RPC as a base and follow the documentation style of LSP. Data models should be documented as commented SQL DDL. ### Run-time type-checking As stated above, `declare` should be used for simple run-time type-checking of public functions. For example, the following: ```lisp (defun f (x) (declare (fixnum x)) (the fixnum (+ x 1))) ``` # Refinement to Production-Quality Software at the level of the previous section is quite usable. It should be confirmed that further improvement is, in fact, required. If so, I argue that there is a repeatable procedure to improve the quality of a (reasonably well-written) prototype to a releaseable product. It may be useful to distinguish two levels of "production-quality". The first limits to widely portable dependencies, but this should be quite capable. The second could use anything (including the Web protocol stack), obviously at a maintenance cost. Ensure that the surrounding infrastructure is in place: * Configuration management. The prototype should already have been checked into git. * Build. Write an ASDF description, and install as a local quicklisp package. * Test. Write FiveAM test cases. Extend the simple run-time type-checking to contracts where possible. * Track. Start using a defect tracking system. Then, the following code & documentation improvements should be made: * Document the system more exhaustively * Can use any of the "starred" libraries in quicklisp. * Maybe "lparallel" to take advantage of all cores Since we have a working prototype, it may make sense to write the documentation (and contracts, and tests) "bottom-up": 1. Contracts, static analysis 2. Test cases 3. Module interface specs 4. Module guide, uses hierarchy 5. Task hierarchy 6. System requirements ## Documentation Details Depend only on GFM, in the same spirit as the software. The use of tools like nw2md and Pandoc should be minised. PlantUML *should* be used where it can replace ad-hoc text. Documents should be stored under git in a "doc" subdirectory of the project. It is recommended to keep the separation between library and UI code, e.g. for using ltk. The following can be added as sections to the README: * Uses hierarchy (but at a module level of granularity) * Task hierarchy And a proper software requirements spec should be written filling in any blanks that the man pages leave. The specification of input and output variables is best left at the level of tables and Basic English again. ### Library This was the subject of [4]. The output artifacts are a module guide an set of module interface specs. However, some of this documentation is better in the source code: * The summary of functions should be taken care of by having the public functions and classes commented. * The formal requirement for function behaviour can be done with tables with [Basic English](https://en.wikipedia.org/wiki/Basic_English). * Although full design-by-contract may be out of reach a poor-man's version can be used with public functions following a pattern. This can also do some of the formal requirements. ```lisp (defun f (x) (declare (fixnum x)) (assert (precondition x)) (let ((res (+ x 1))) (assert (postcondition res)) (the fixnum res))) ``` `lisp-critic` can be used to perform static analysis of the codebase. But it's not worth writing custom rules. ## Dependencies For productisation you may want to add more features. Although the official ANSI standard is moribund, "community standard" (i.e. starred) libraries are recommended on the [Awesome-CL list](https://github.com/CodyReichert/awesome-cl), or [CL portability layers](http://portability.cl/). Dependencies should be limited to these two initially. Ltk can implement a GUI to replace the prototype command-line or terminal-based UI, if it makes sense. A second round of productisation, which again may never actually be required, could include: * Any of the `trivial-` libraries from the Awesome-CL list. The `trivial-` libraries may be *forked* and maintained locally. * Any other `trivial-` libraries available in Quicklisp. * Other libraries available in Quicklisp. Now it may also be worth taking on the complexity of a Web UI, using HTMX and the platform.sh stack. HTMX & ReST (following Fielding) seem simpler than single-page applications (outside the very specific case of drawing on a canvas using ParenScript). ## Testing Unit (FiveAM) tests grow in parallel with the module interface specs. Property-based testing would be nice here, but there doesn't seem to be a readily-available library. System tests grow in parallel with the requirements spec. It's ok for system tests to use the same interfaces as the ltk code. All tests should be automated, except possibly for the UI/view layer. Q: These scripts could be generated from a literate test plan? A: yes, probably one of the few places to use nw2md. As much of the testing work should be pushed "back" in the V model to contracts for the functions, following the pattern above. # Conclusion A method for developing software from an incomplete understanding of the requirements is given. It is hoped that this is more effective than most of what is currently-used. # References [1]Kent Beck, Extreme Programming Explained (1999). [2]David Robertson and Jaume Agustí, Software Blueprints: Lightweight Uses of Logic in Conceptual Modelling, Addison Wesley Longman (1999). [3]Kent Pitman, Accelerating Hindsight: Lisp as a Vehicle for Rapid Prototyping (1994). [4]Darren Bane, Design and Documentation of the Kernel of a Set of Tools for Working With Tabular Mathematical Expressions, University of Limerick, Ireland (19 Jul 2008). [5]Darren Bane, An ISLisp-like subset of ANSI Common Lisp, Ireland (21 Aug 2020). [7]Kristaps Dzonsons, Practical UNIX Manuals. available: https://manpages.bsd.lv/toc.html [accessed 9 Oct 2020].