Building and Testing Apps Productively

How do you build a web project from beginning to end? Following this workflow personally helps me to identify potential issues ahead of time. Premature optimization is one of the biggest killers of productivity, and the more layers of abstraction in your head between design and product, the more impossible the project seems. This workflow has helped me to be more productive and avoid some of these issues.

  1. Start with the design
    Whether this means wireframes or using a design template, having the design finalized allows for refinement and polish. The alternative would be to build and design each feature and view together progressively. That ultimately ends up creating a product that serves as an excellent starting draft for a future revision, but a finalized version cannot be built this way.

  2. Start with the least amount of technology layers
    At the end of the day, websites are downloaded strings, validated and interpreted against an HTML spec to compose the display shown by your browser. That also means that you can build the finalized frontend HTML markup independently before building any backend or auth flow.

  3. Add one layer of technology at a time
    Once you start building in HTML, you reach a point where you need to start introducing loops and dynamic renders. This is your first foray into the needs of a frontend framework. Backend calls should still be flat JSON files until you reach a point where that becomes tedious.

  4. Auth Comes Last
    It's common to conceptualize an app from beginning to end and start with the auth flow. This is the biggest killer of time and motivation. You want to add auth once there is something to protect, not because it's your first mental image of the app. Authentication is a productivity offramp, and it can quickly be a demoralizing distraction where after days of work, all you have accomplished is an auth flow. Focus on what the customers care about most -- the actual experience of using this interaction and what was done before that can now be done better.

  5. Mock -> Build -> Prototype
    If you're familiar with frontend testing frameworks, adding testing to an existing app is hard to justify with all of the extra boilerplate code necessary to run basic tests. Mocks, spies, specs, it gives an impression of practice without theory. However if you reverse the approach and remove the backend, you find these tools work exceedingly well. When building interactions between frontend and backend, you really want to validate:

  • The client-side logic is bug-free and functioning as expected (specs and unit tests)
  • The API call is made (spies)
  • We have designed a data contract interface between client and server
  • The client can handle the variety of responses returned from the server (stubs)

These four types of frontend testing compose the entire lifecycle of an interaction and network request. When these tests break, they flag to your engineers that an underlying assumption and promise was broken. Unless these assumptions are written down as tests, they are only known if the author is available. Some companies choose to put this knowledge in Wikis and knowledebases. However, the further you abstract the documentation from the implementation the less resourceful that documentation becomes. Testing allow for a rare combination of vernacular for humans and code for machines, all adjacent to the underlying source of truth (the code).

Starting a project with a mock and build approach helps solve some of these common types of challenges:

  • Prevent confusion of responsibility between frontend and backend, helping enforce a singular source of truth
  • Iterate between design and prototype quickly with reduced complexity per change
  • Reduce team / contributor dependency while simultaneously vitalizing collaboration between teams.
  • Allowing development of features and forcing decisions to long term data transformation challenges early and transparently
  • Improves offline-only developer experience

It's difficult to take an existing legacy project and retrofit these testing technologies in hopes of gaining these benefits. This is not a technical limitation but rather the consequences of the iterative diminishment of human morale and delayed gratification.