We are a software consultancy based in Berlin, Germany. We deliver
high quality web apps in short timespans.

Upstream Agile GmbH

12 Rules for Writing Web Apps in 2012

May 12, 2012 by Alexander Lang

The following is a list of ideas I pulled out of my head over the course of today. While it was slapped together rather spontaneously it is based on six years of writing my own web apps, looking at others’ code and basically a lot of headscratching and crying.

None of these ideas are new, others have written about them over and over. Still most of the world around me is stuck in uncomprehensible code, coffee breaks for running tests and banging their head against the wall.

The intention of this post is not to piss on anyone’s coding style but to lay a foundation for writing better apps in the future – for myself and hopefully for others, too. The code examples are in Ruby but most of it should apply to other OO languages as well.

Business Models

Your business models are the core of your application. They model your business domain and nothing else.

1. Models are plain old Ruby objects.

They don’t extend a framework base class like ActiveRecord::Base.

2. Your models do not persist themselves.

Instead they use delegation. This decouples them from your persistence framework and makes them unit testable.

Don’t do this:

class User::ActiveRecord::Base
  def update_profile(attributes)
    ...
    save
  end
end

Instead do this:

class User
  def update_profile(attributes)
    storage.load(user_id).update_attributes(attributes)
  end
end

3. All execution flow is explicit.

No Callbacks. The various lifecycle callbacks most persistence frameworks now offer make your life easier if you have four models with 10 lines of code each. In larger apps they lead to unpredictable chains of code execution and tight coupling.

Don’t do this:

class User
  after_save :create_welcome_message

  def create_welcome_message
    Message.create! text: 'Welcome User!'
  end
end

Instead do this:

class Signup
  def complete
    user.confirm
    Mailbox.create_welcome_message_for(user)
  end
end

4. Models do not expose their properties publicly.

Instead they offer an API for others to use.

Don’t do this:

Comment.create author: user.name

Instead do this:

user.sign comment

5. Each model encapsulates its persistent data.

If other objects want to access that data they have to ask the respective model.

Don’t do this:

class Comment
  def create
    ...
    storage.load_user(author_id).comment_count += 1
  end
end

Instead do this:

class Comment
  def create
    ...
    user.notify(:comment_created, self)
  end
end

Persistence

6. Use whatever database or service is best for the current job.

Don’t be afraid to use more than one. Don’t use a relational DB for full text search. Use a full text search engine. Don’t store logging data in your main database, use a logging service. And don’t kill -9 your MongoDB.

Some of you data may be write heavy, other read heavy and in need of caching. Research your options, don’t just rely on what’s the default. It will make your life easier and your app faster.

Front-end

7. Your front-end is a RESTful JSON API.

The internet is meant to be connected. Data must be free. Apps need to communicate. You want to build your service on top of others. Others will want to build on top of you.

A proper RESTful JSON API is easy to explore and understand. REST scales, it’s what the internet is made of.

Your human facing front-end is either a client side JavaScript app or another server side app.

8. Your app provides OAuth2 authentication.

OAuth2 is easy and open. It has security built in. It builds the basis for others to use your API.

Testing

9. Your API is fully tested with integration tests.

This is 2012.

10. Your business models are unit tested.

As in real unit tests, no database access in tests. There will be many, they must run fast.

11. Your tests run within two minutes.

If they are slower make them faster. If you can’t make them faster split your code base. There is not enough going on on Twitter and Hacker News to wait for slow tests. More importantly your customer shouldn’t pay you waiting.

Frameworks

12. Prefer small and simple

Rails is great, but do you really need all of it? Since 3.0 it has been split into smaller parts. Maybe Sinatra + ActiveRecord will do the job? A 50 line Gemfile may be impressive but it sure hosts a ton of bugs. Less code means faster, simpler, error free-er.

The bottom line

I can’t promise you anything but these are the effects I’m trying to achieve.

Testability

By decoupling objects they can be truly unit tested individually. Your tests will be easy to write and they will run fast.

Scalability:

  • Of your code base – decoupling means you can split your code base into smaller apps as it grows. Avoid the big spaghetti monster.
  • Of your team – instead of one large project you now have many small ones that more people can work on simultaneously without stepping on each others’ toes.
  • Of your storage layer – by using the right tool for the job your tools will get your further. A lot further than abusing MySQL into doing things it was never meant to do.

Flexibility

Your main app being an API means you can add mobile apps and other clients easily. Your components being independent means you can swap backends or frameworks and even programming languages.

Happiness

Fast tests, easy to understand relations, small code base – pain free coding, short development cycles, bring out the unicorns.

Conclusion

I have tried these ideas out to variying degrees. I will continue to do so over the next months. Maybe I’ll start crying again. Then it’ll be time for another post. Until then, let’s move forward, let’s write better software. For the unicorns.


Follow me on Twitter: @langalex, Github: http://github.com/langalex