Code a fully-functional web app in 14 days…

Challenge Accepted

If your friend asked you to build a fully-functional, feature-complete MVP in 14 days how would you respond? This was the exact question posed to me by my roommate, Benjamin Hoffman, three weeks ago. I answered:

Get me some Philz Coffee, pump up that house music, and let’s whiteboard it out!

We needed to roadmap, design, architect, and code everything within two weeks. How the hell were we were going to pull this off? Check out his post!

The Quick and Dirty

The TL;DR of our adventure

We agreed from the start that I would lead development and Ben would lead product. You can read more about his story in From 0 to Product in 14 Days.

Because we didn’t have a lot of time, we had to follow a very logical process. We gave ourself one KPI: ship product. All roads would lead to us shipping the best product we could given our time constraint. After discussing responsibilities, we planned our roadmap and scoped our feature set. Here is what we settled on:

  • We want our users (investors) to be able to add startups or founders to their pipeline.
  • Once added, the investor should be able to attach notes to either the startup or founder.
  • We also want to make this process easy for them. This means pre-populating form fields and leveraging third party APIs whenever possible.
  • Lastly, because investors manage their pipeline of applicants like a pipeline, we wanted to provide them a feature to put the startups in a funnel.

This might sound barren but it represents the “minimum” in Minimum Viable Product (MVP). It also helped us achieve our goal of shipping a functional product.

Okay I get it, so what’s your tech stack?

When we looked at the situation holistically, we knew we had to choose something practical that would allow us to get up and running as soon as possible.

This is why we chose Angular for our frontend with Firebase for our backend. For styling we used Bootstrap and for additional functionality we used Angular UI.

This stack allowed us to focus exclusively on building features for our users. We strongly advocate going this route if you are building an MVP or prototype. Your users most likely don’t care about your data structure, how scalable your server-side language is, or what framework you used.

If you are building software for other people to use, build something that works, is fast, and meets their needs.

Angular + Firebase met all our needs.

WHY U NO REACT?

So why didn’t we use React to build our app? That’s a great question because not only is React the newest rave amongst all the kids these days, it’s faster, much more scalable, and lighter weight.

React is great but if you had to ship a feature complete app under a tight deadline, which framework would you choose? We chose Angular because it’s time and tested, has a robust online community, and has plentiful plugins to help us serve a feature-rich experience.

That said, refactoring into React is our v2 roadmap (see ending).

Directory Structure & Foundation

Modularized by Feature and Function

The proper way to organize your files has been argued over and over. After a brief discussion, we decided to organize our files by feature for modularity. This allowed us to code-away without fear of merge conflicts while rebasing after pull requests. Additionally, we felt this structure would work best for scalability both in features and the addition of future team members.

├── app
│ └── about
│ ├ ├── about.html
│ ├ ├── about.js
│ └── addEntry
│ ├ ├── addEntry.html
│ ├ ├── addEntry.js
│ └── assets
│ ├ ├── charts
│ ├ ├── logo_blank.svg
│ ├ ├── venture-route-logo-small.png
│ └── auth
│ ├ ├── auth.js
│ └── bower_components
│ └── common
│ ├ ├── addNoteFactory.js
│ ├ ├── angelListFactory.js
│ ├ ├── dashboardFactory.js
│ ├ ├── fullContactFactory.js
│ ├ ├── phoneFilter.js
│ ├ ├── profileFactory.js
│ └── dashboard
│ ├ ├── dashboard.html
│ ├ ├── dashboard.js
│ └── env
│ ├ ├── config.js
│ └── footer
│ ├ ├── footer.html
│ └── home
│ ├ ├── home.html
│ ├ ├── home.js
│ └── nav
│ ├ ├── nav.html
│ ├ ├── nav.js
│ └── note
│ ├ ├── addNote.html
│ ├ ├── addNote.js
│ └── profileFounder
│ ├ ├── profileFounder.html
│ ├ ├── profileFounder.js
│ └── profileStartup
│ ├ ├── profileStartup.html
│ ├ ├── profileStartup.js
│ └── styles
│ ├ ├── main.css
│ ├── app.js
│ ├── index.html
├── documentation
├── test
├── .bowerrc
├── .gitignore
├── bower.json
├── npm-debug.log
└── README.md

If you glanced over our directory structure and thought to yourself — “this could be a little more modular”, you’d be right! As I mention towards the end of this post, one of our next steps is to refactor our codebase for dryness while breaking our file structure into smaller reusable chunks.

Feature Complete Apps Make for Great MVPs

Or, how Angular plugins give you super powers

Before the flaming or trolling starts because of our over-reliance on plugins, remember our KPI (ship product in 14 days). And let’s all be honest with ourselves, the Angular UI community has built awesome plugins that have been around for awhile and are well maintained.

With that said, here is a highlight of our favorites:

Angular xEditable

Inline editing of table fields and startup/founder profiles makes for great UX and cuts down on unnecessary clicks — thus making our user’s lives a bit easier.

Angular Moments

Based on the popular moments.js library, Angular moments reformatted all our dates to human readable format. Maybe not the coolest feature we implemented, but it’s the small things like this that make our app delightful to use.

<td class=”col-sm-3" am-time-ago=”note.date”></td> 
<!-- example: '2 days ago' -->

Angular Autocomplete

Why put the burden on our users to type a startup’s location with proper address formatting. Angular Autocomplete (powered by Google Places API) is another delightful feature we included to make it easy for an investor to add new startups to their Venture Route pipeline.

Server & Backend

Ah, good old Firebase

We could write forever about this. In short, Firebase is so easy to get started immediately out of the box. We were hitting the API within minutes.

Firebase is a full-service backend. This means it takes care of everything from API to data storage. All data is stored as a JSON object and in addition to supporting a RESTful API, it goes a step further and provides its own robust API. What’s more, it offers AngularFire, a service that automatically keeps local objects or arrays in sync with any changes made to the remote Firebase database. After some headbanging against the desk this feature played very nicely with our inline editing feature (above) — allowing changes in the view to persist to the database, then the controller, and back to the view again.

function($q, $firebaseObject, $firebaseArray) {
// Set all ref URL’s
var notesRef = new Firebase(‘https://pipeline8.firebaseio.com/notes/');
  // define variables to store returned data
var _notesArr = [];
  return {
getNotes: function() {
var defer = $q.defer();
      notesRef.on(‘value’, function(data) {
      // firebase snapshot of our notes database
notes = $firebaseArray(notesRef);
      // on resolve, pass the startups array to our controller
defer.resolve(notes);
});
    return defer.promise;
},
},

What’s my Overall Favorite Feature?

AngelList API Integration!

I cannot stress enough how important it was for us to provide our users with a platform that boasts limited input requirements, clicks, and navigation. Given we are building a product for VC’s, who possibly receive applications in the thousands per year, it was important that the investors flow through our funnel smoothly and quickly.

Whether it was adding a new startup, new founder, or new note, we made only one field required per form. We also pre-populated input fields at every opportunity. Lastly, and something we are very excited about, we leveraged third party APIs to help bring in more profile information.

For the startup profile, the only required field is startup name. Now here’s the cool part, once a startup is added the investor has the option of pulling in all relevant data via AngelList API! If you haven’t checked out their API, get on it because the data available is enough to make any tech or startup junky geek out. Many thanks to the team at AngelList!

AngelList requires you to know the startup ID in order to pull in a full profile, so we opted on making two API calls. The first queries their database by startup name, we then parse the response, iterate over the first six objects (startups), and display six logos to choose from. Upon clicking the correct logo, we query the API again for that specific startup via the startup ID. This is my favorite feature because it’s simple, elegant, and informative.

Pretty cool, right?

What’s Next

Version II of our app is going to rock

From a tech standpoint, we have a ton planned!

For starters, now that we have more time, we would love to refactor into React. There are so many reasons why (briefly mentioned above) and now that we aren’t in such a rush, we have time to build for scale.

While we are refactoring into React, we will be drying up the code and breaking our file structure into smaller and more reusable, modular chunks.

In terms of new features, we’ve got a lot on our roadmap. We’ve chatted about adding @mention ability as well as #hashtags. This makes it easier to organize content.

We also need to rethink and reformat the way we implemented notes. We see adding, editing, and removing notes as more of a conversation rather than static one-off text blocks. Think Asana but for CRM. This means we need to rewrite this from the ground up to include threading.

We also would like to be better at TDD — enough said.

ps. thank you Rob Rav for letting us pick your brain, and James Basnett for the awesome logo, and James Brewer for the late-night Javascript chats.


This post was written by Ryan Haase and edited by Benjamin Hoffman. Both are passionate about startups and love working in fast-pace, collaborative environments.

If you’d like to chat further about our story, email Ryan at ryandhaase@gmail.com

If you’ve read this far, show us some love by clicking the below.