Web application development workflow with Node.js

Context

The aim of the article is to describe a good workflow to use when developing web applications. I always read advice on good workflow practices but they are never aggregated together, that’s why I do it right now.

UI Driven Development

We are going to follow a Behavior Driven Development style. Moreover we are making a web application, so we decide to focus on user: we will start by writing code for the UI. Then for UI and backend, we will write our specs/tests first.

Technos

As technology we are going to use :

  • Node.js and its framework Expess.JS because of their popularity and to get more familiar with it.
  • The language is Coffeescript for its readability.
  • Backbone is the UI framework.
  • Mongoose is the ODM.
  • Git is the version control system.
  • Jasmine is the BDD framework for UI and Vows is the BDD Framework for backend.

Use case

The application on which I will apply the workflow is Ponyo, a simple app that actually does nothing apart of allowing to create and browse “categories”. To illustrate the workflow we are going to add a new feature : allow category deletion.

Category list

Category display

Workflow

Here are all steps we need to do proper developments. Of course most of the time pressure push us to do shortcuts, but it is good to keep the good way in mind. Moreover this complete workflow could help to think about what could be optimized/automatized:

  1. Make a branch
  2. Write UI specs + commit
  3. Write UI specs code + commit
  4. Write UI code + commit
  5. Write backend resource specs + commit
  6. Write backend resource tests + commit
  7. Write backend resource code + commit
  8. Run all tests
  9. Test your app manually
  10. Rebase branch
  11. Merge branch + push
  12. Refactor if needed

Details

1. Make a branch

First of all, don’t bother other programmers right now with our commits, so let’s create a branch called feature-delete-category.

With Git

    git branch feature-delete-category
    git checkout feature-delete-category

2. Write UI Specs

We are doing UI Driven Development, so first write client side code specs. Then, check with your browser (here the URL is http://localhost:3000/tests/) that the Jasmine entries fail.

With Jasmine

describe 'Category deletion', ->
  it 'When I display newly created category', ->
      expect(false).toBeTruthy()
  it 'And I click on delete category button from a category page', ->
      expect(false).toBeTruthy()
  it 'Then it brings me back to category list', ->
      expect(false).toBeTruthy()
  it 'And deleted activity is no more in the list', ->
      expect(false).toBeTruthy()

With Git, commit

    git commit tests/categories.coffee -m "Add specs for category deletion."

3. Write UI specs code

Now that you know what you want to do, you can write corresponding tests and checks that they fail.

With Jasmine

describe 'Category deletion', ->
it 'When I display newly created category', ->
    runs ->
      $("#category-jasmine").click()
    waits(500) # Waits to be sure that everything is done before testing
it 'And I click on delete category button from a category page', ->
    runs ->
      $("#delete-category-button").click()
    waits(500) # Waits to be sure that everything is done before testing
it 'Then it brings me back to category list', ->
    runs ->
      expect($("#category-list").length).not.toEqual 0
it 'And deleted activity is no more in the list', ->
    runs ->
      expect($("#category-jamsine").length).toEqual 0

With Git, commit

    git commit tests/categories.coffee -m "Add tests for category deletion."

4. Write UI code

Now we are going to write the UI code, it is needed to know what we expect from server. We add a button to the template displaying a category, then we code the button behavior. After that we check that our tests still fail (backend does not support request for deletion).  Finally we commit.

Modify the template with Eco

<p>
  <a id="delete-category-button" href="#home">
    Delete category<br />
  </a>
</p>

Write behavior with Backbone

categoryViewTemplate = require('../templates/category_view')
Category = require('../models/category').Category

class exports.CategoryView extends Backbone.View
  id: 'category-view'

  constructor: ->
    super()

 render: (category) ->
   $("#nav-content").html null
   $.get "/categories/#{category}/", (data) =>
     $("#nav-content").html categoryViewTemplate(category: data)
     @model = new Category data
     @deleteButton = $("#delete-category-button")
     @deleteButton.click(@onDeleteButtonClicked)

 onDeleteButtonClicked : (event) =>
   event.preventDefault()
   @model.destroy
     success: ->
       app.routers.main.navigate(&quot;home&quot;, true)
     error: ->
       alert "An error occured, category was probably not deleted."
       app.routers.main.navigate("home", true)

With Git, commit

    git commit public/ -m "Add deletion button to UI"

5. Write backend resources specs

Now we know that we need  a resource to delete category, so let’s write our category deletion resource specs and commit.

With Vows

.addBatch
  'DELETE /categories/category-02/':
    topic: () ->
      apiTest.del 'categories/category-02/', @callback

    'response should be with a 200 OK': (error, response, body) ->
      assert.ok false
    'GET /categories/category-02/':
      topic: () ->
        apiTest.get 'categories/category-02/', @callback
      'response should be with a 404 Not Found': (error, response, body) ->
        assert.ok false
    'DELETE /categories/category-02/':
      topic: () ->
        apiTest.del 'categories/category-02/', @callback
      'response should be with a 404 Not Found': (error, response, body) ->
        assert.ok false

With Git

 git commit test/ -m "Add backend resources specs"

6. Write backend resource tests

Now we write our test code, we just check that returned HTTP code are expected ones and that once category is deleted, it cannot be reached anymore. We commit.

With Vows

.addBatch
  'DELETE /categories/category-02/':
    topic: () ->
      apiTest.del 'categories/category-02/', @callback

    'response should be with a 200 OK': assertStatus 200
    'GET /categories/category-02/':
      topic: () ->
        apiTest.get 'categories/category-02/', @callback
      'response should be with a 404 Not Found': assertStatus 404
    'DELETE /categories/category-02/':
      topic: () ->
        apiTest.del 'categories/category-02/', @callback
      'response should be with a 404 Not Found': assertStatus 404

With Git

 git commit test/ -m "Add backend resources specs code"

7. Write backend resource code

Now we write code : we add a new route that will link to a new resource dedicated to category deletion. We commit.

With Express

  app.del "/categories/:category/", routers.deleteCategory

With Express and Mongoose

exports.deleteCategory = (req, res) ->
  categoryProvider = new CategoryProvider

  categoryProvider.getCategory req.params.category, (err, docs) ->
    if err
      console.error(err.stack)
      res.json 'An error occured', 500
    else if docs.length > 0
      docs[0].remove (err) ->
        if err
          console.error(err.stack)
          res.json 'An error occured', 500
        else
          return res.json  success: true
    else
      res.json 'I dont have that', 404

With Git

 git commit test/ -m "Add category deletion resource"

8. Run all tests

We run our backend tests and our UI tests through browser and we are glad to see they all work.

With Vows

vows --spec test/resources.coffee

9. Test your app manually

Once you launch all your tests, test your application as a normal user. BDD is great but it will never replace a manual test, we often miss something that is not revealed by our tests.

Category deletion is possible


10. Rebase branch

We want to add our commits like we did them from last version of master branch, so we use rebase command.

With Git

git rebase master

11. Merge branch

Then we can merge our features to the master trunk. A push to master branch will validate that work is done !

With Git

git checkout master
git merge feature-delete-category
git push
git branch -d feature-delete-category

12. Refactor if needed

UI Driven development has the nice advantage to not let you develop unuseful resources but it does not let you think as good as possible the way to develop your backend. So you will probably need some refactoring. Fortunately, with your tests refactoring will be easier and safer. Moreover patterns you see when you develop UI first push you to think about refactoring that match better to your needs.

NB: Feel free to comment and criticize this article so I could improve it and correct what is wrong.

13 Responses to Web application development workflow with Node.js

  1. Way over my head.

  2. Sven says:

    Perhaps your workflow could be improved using gitflow. If you google for it, you’ll find documentation, tutorials, a screencast and the source code on github of course.

  3. Love it. Nice and formal, but flexible.

  4. If you commit when your tests are failing, you can’t use git-bisect to determine when regressions happened.

    Otherwise, great post!

  5. Jody says:

    Generally you don’t want to commit the results of compilation into a revision control system, just the source. In this case that means committing the .coffee but not the .js. The .js can change due to changes in the CoffeeScript compiler, and diffing it isn’t meaningful.

    • Gelnior says:

      You’re right. I removed javascript file from commit. For other part we could consider “test/*.js” and “public/client/build” in the .gitignore.

  6. mbrochh says:

    Great post! I would like to suggest to use “git merge –no-ff feature_branch_name” at the very end. If you do that, you will later be able to see that this bunch of commits has been developed in a feature branch.

    Somehow I like this better than just having one big plain chain of fast forwarded merges.

  7. Hi!

    I’m the web editor at iMasters, one of the largest developer communities in Brazil. I´d like to talk to you about republishing your article at our site.

    Can you contact me at rina.noronha@imasters.com.br?

    Bests,
    Rina Noronha
    Journalist – web editor
    http://www.imasters.com.br
    redacao@imasters.com.br
    rina.noronha@imasters.com.br
    +55 27 3327-0320 / +55 27 9973-0700

  8. saransh says:

    very nice and helpfull.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: