An elegant way to log with a Ruby block

January 2nd, 2010

Imagine you created a generator much like Rails. It creates a series of directories and copy a set of files to a specific location. Some things may fail, and you need a nice way to show what’s going on behind the scenes.

I was browsing some code on GitHub and found an elegant approach to this problem.


module Glue
  module Generator
    extend self
    extend Glue::Helper

    def generate!(options={})
      templates = File.expand_path(File.dirname(__FILE__) + "/../../templates")
      output = File.expand_path(options[:path])

      # create root directory
      run "creating #{Colorize.yellow(output, :style => :underscore)} directory" do
        FileUtils.mkdir_p(output)
      end

      # create javascripts directory
      run %(creating #{Colorize.yellow("public/javascripts")} directory) do
        FileUtils.mkdir_p File.join(output, "public/javascripts")
      end

      # create images directory
      run %(creating #{Colorize.yellow("public/images")} directory) do
        FileUtils.mkdir_p File.join(output, "public/images")
      end

      # create views directory
      run %(creating #{Colorize.yellow("views/layouts")} directory) do
        FileUtils.mkdir_p File.join(output, "views/layouts")
      end
    end
  end
end

module Glue
  module Helper
    def run(message, &block)
      $stdout << "\n#{message}... "

      begin
        yield
        $stdout << Colorize.green("done!")
      rescue Exception => e
        $stdout << Colorize.red("error ~ #{e.message}")
      end
    end
  end
end

This code does a few interesting things:

  • the “run” method is defined inside a helper module. It begins by receiving a message (what are you doing right now?) and a block. It starts by giving control to the caller with a yield (now the execution inside the “run” method is halted). So the block passed to the run method is now executed. This block is actually the lines you see above between “run … / end”;
  • So in this block you can “do your thing”. For example, create a directory, request some info from a remote API, or whatever;
  • When the block is finished running, the lines after yield (in the “run” method) are executed. If no exception is raised a Ok message is printed, otherwise we’ll see what went wrong with the specific exception message.

This is very cool. You can now execute failure-prone tasks in a very elegant way, and with a really nice syntax :)

This code was created by Nando Vieira in an interesting project called Glue, which is a simple static site generator for Ruby that uses Haml, Sass and Textile/Markdown. The documentation is here.

You can see the code samples above, here and here.

Reasons to use a no-sql database like mongoDB

December 26th, 2009

In this article I’ll talk about reasons to use mongoDB and other no-sql databases.

Relational databases are often used in many web apps. But usually when the time comes to scale the app to a few millions users, you have to make choices on your architecture.

For instance, a common practice to handle high load is to put one or more of the most used tables in separate servers. In order to use this technique, a developer will have to resolve the issue of join queries. How do you query tables that aren’t in the same location and yet have high performance?

One solution is to denormalize data, thus duplicating content in all tables that may have a query that needs to know about it. Think of 2 tables: contacts and users. The tables are separated into 2 different servers. And you have a feature in your app, in which you show all contacts from a specific user. You need a join query, but you can’t use it. So you duplicate some of the fields from the contacts table, right inside your users table. For instance, you create a field called contact_names, in which you put only the names, of all contacts from that user, separated by commas. It’s a easy way to solve the problem, but it comes with a cost. You have to worry about syncing the contacts in all tables that know something about contacts.

Bottom line? You started developing your app with join queries, but at some point you had to give up on it.

So, if using a traditional database forces you to stop using some of its features somewhere down the road, why not start with a kind of database that avoids the things that are not scalable and sustainable in the long run?

In mongoDB a solution for this problem would be creating a document Contacts, and embed it inside the document Users. So, each user will have its contacts right there, inside each one of the User records. No need to use join queries.

However, there are times when you need to have a model that is connected to several others.

For example, let’s say you need to relate Contacts to several models such as Clients, Suppliers and Employees. So you create 4 collections: Clients, Suppliers, Employees and Contacts. You connect them all together via a db reference. This acts like a foreign key. But, this is not the mongoDB way to do things. Performance will penalized.

So the general question should always be “Why can’t I embed this document?“. Or even better: “Does this object merit its own collection, or rather should it embed in objects in other collections?“.

There are some general rules on when to embed, and when to reference (grabbed from mongodb website):

  • “First class” objects, that are at top level, typically have their own collection;
  • Line item detail objects typically are embedded;
  • Objects which follow an object modelling “contains” relationship should generally be embedded;
  • Many to many relationships are generally by reference;
  • Collections with only a few objects may safely exist as separate collections, as the whole collection is quickly cached in application server memory;
  • Embedded objects are harder to reference than “top level” objects in collections, as you cannot have a DBRef to an embedded object (at least not yet);
  • It is more difficult to get a system-level view for embedded objects. For example, it would be easier to query the top 100 scores across all students if Scores were not embedded;
  • If the amount of data to embed is huge (many megabytes), you may reach the limit on size of a single object;
  • If performance is an issue, embed;

The way I see it, you can still have more or less the best of both worlds: the flexibility of documents and the performance of embedded documents. And you still have a way to emulate foreign keys, like a relational database - but not without a penalty on performance. I don’t know how mongoDB and MySQL compare to each other in the long run, for the usual web app. It’d be cool if someone did some benchmarks on this subject.

Read more about mongoDB database schema design.

Playing with mongoDB

December 25th, 2009

It’s been a long time I hear about mongoDB and its awesomeness, so now’s the time to play with it! :)

In this article I’ll try to summarize what’s mongoDB, why it’s so cool, and how to start playing with it.

What is mongoDB?

mongoDB is one of the new NO-SQL databases. It means it’s not record-oriented like relational databases. Instead, it’s schema-free and collection oriented. And also, it’s a document database.

So what’s a document? It’s the first unit of data in a mongoDB. A document is simply an array of key-value pairs like this:


{
     name: "John Doe",
     age: 40
}

Note: we call this notation BSON, which stands for “Binary Serialized dOcument Notation”.

Back to the document. This “document” is similar to a record in a usual relational database. You can use Strings, Integers and many other data types, including arrays and other Documents. You have the ability to nest Documents, like, Person -> Children -> Toys.

So what’s a table then? Collections act more or like tables. A collection holds one or more documents.

And ultimately a database is a group of collections. Each collection has a unique name inside a database.

With the document stuff out of the way, let’s see why this database seems to cool.

Why is mongoDB so cool?

Working with a no-SQL database means you have several advantages over a traditional database. Here are some of them:

  • there is no schema. You don’t have to use Rails migrations for creating tables and columns. You simply start using them, and the database creates them on the fly. When using the gem mongomapper, for example, you simply declare the keys inside your model, and that’s it. Simple like that;
  • the data is formatted using a JSON-like format, giving greater flexibility and at the same time simplicity. New data types can be added, depending on how your format your Document before saving it into the database;
  • storage of binary files such as videos and photos on the database is possible and more important, efficient;

Basically, mongoDB bridges the gap between key-value stores (which are highly scalable) and traditional RDBMS (which provide structured schemas and powerful queries).

Other than these things, mongoDB also supports some types of database replication. It also offers auto-sharding, a feature that allows one to build a large horizontally scalable database cluster that can incorporate additional machines dinamically. It also supports map-reduce.

Installing mongoDB

Simply download the binaries. Unpack the tar.gz to /usr/local/bin/mongo and add ‘/usr/local/bin/mongo/bin’ to your PATH. Create the directory to which the database files will be saved to:

mkdir -p /data/db

Start the server with:

mongod

It listens for connections on port 27017 by default. Open another shell and start the mongo shell with:

mongo

Interesting enough, the mongo shell uses Javascript as its language :)

No need to create a database

One important note. You never have to create a database or collection.

The moment you try to access a database or collection, the underlying database and/or collection is created automatically.

Let’s add some data

In your mongo shell, type the following:


> a = { brand: "Toyota", model: "Corolla" };
> b = { name: "John", age: 40 }
> db.things.save(a);
> db.things.save(b);

Now to query all saved documents inside the collection things, run:

> db.things.find();
{ "_id" : ObjectId("4b33dee3844fab562308bb5f"), "brand" : "Toyota", "model" : "Corolla" }
{ "_id" : ObjectId("4b33dee5844fab562308bb60"), "name" : "John", "age" : 40 }

Important things to note:

  • the collection things is created automatically;
  • the documents inside a collection may have different scructures, as you can see these 2 documents have different fields;
  • upon being inserted into the database, objects are assigned an object ID in the field _id;
  • when you run these commands above, the object IDs will be different.

Adding more data

You noticed by now we are using Javascript inside the mongo client/shell. This means you can use something you already know to interact with mongoDB.


> for( var i = 1; i < 10; i++ ) db.things.save( { x:4, j:i } );
> db.things.find();
{ "_id" : ObjectId("4b33dee3844fab562308bb5f"), "brand" : "Toyota", "model" : "Corolla" }
{ "_id" : ObjectId("4b33dee5844fab562308bb60"), "name" : "John", "age" : 40 }
{ "_id" : ObjectId("4b33df5b844fab562308bb61"), "x" : 4, "j" : 1 }
{ "_id" : ObjectId("4b33df5b844fab562308bb62"), "x" : 4, "j" : 2 }
{ "_id" : ObjectId("4b33df5b844fab562308bb63"), "x" : 4, "j" : 3 }
{ "_id" : ObjectId("4b33df5b844fab562308bb64"), "x" : 4, "j" : 4 }
{ "_id" : ObjectId("4b33df5b844fab562308bb65"), "x" : 4, "j" : 5 }
{ "_id" : ObjectId("4b33df5b844fab562308bb66"), "x" : 4, "j" : 6 }
{ "_id" : ObjectId("4b33df5b844fab562308bb67"), "x" : 4, "j" : 7 }
{ "_id" : ObjectId("4b33df5b844fab562308bb68"), "x" : 4, "j" : 8 }
{ "_id" : ObjectId("4b33df5b844fab562308bb69"), "x" : 4, "j" : 9 }

Iterating the data using the cursor

When we ran db.things.find(); in the last example, the shell automatically showed all data from the collection. But if we assign a variable to the find() method, we can iterate the data it as we wish:


var cursor = db.things.find();
> cursor.next()
{
        "_id" : ObjectId("4b33dee3844fab562308bb5f"),
        "brand" : "Toyota",
        "model" : "Corolla"
}
> cursor.next()
{
        "_id" : ObjectId("4b33dee5844fab562308bb60"),
        "name" : "John",
        "age" : 40
}
> cursor.next()
{ "_id" : ObjectId("4b33df5b844fab562308bb61"), "x" : 4, "j" : 1 }
> cursor.next()
{ "_id" : ObjectId("4b33df5b844fab562308bb62"), "x" : 4, "j" : 2 }

Or we can use iterate programmatically:


> var cursor = db.things.find();
> while (cursor.hasNext()) {print (tojson(cursor.next())); }
{
	"_id" : ObjectId("4b33dee3844fab562308bb5f"),
	"brand" : "Toyota",
	"model" : "Corolla"
}
{
	"_id" : ObjectId("4b33dee5844fab562308bb60"),
	"name" : "John",
	"age" : 40
}
{ "_id" : ObjectId("4b33df5b844fab562308bb61"), "x" : 4, "j" : 1 }
{ "_id" : ObjectId("4b33df5b844fab562308bb62"), "x" : 4, "j" : 2 }
{ "_id" : ObjectId("4b33df5b844fab562308bb63"), "x" : 4, "j" : 3 }
{ "_id" : ObjectId("4b33df5b844fab562308bb64"), "x" : 4, "j" : 4 }
{ "_id" : ObjectId("4b33df5b844fab562308bb65"), "x" : 4, "j" : 5 }
{ "_id" : ObjectId("4b33df5b844fab562308bb66"), "x" : 4, "j" : 6 }
{ "_id" : ObjectId("4b33df5b844fab562308bb67"), "x" : 4, "j" : 7 }
{ "_id" : ObjectId("4b33df5b844fab562308bb68"), "x" : 4, "j" : 8 }
{ "_id" : ObjectId("4b33df5b844fab562308bb69"), "x" : 4, "j" : 9 }

Or use it like an array:


> var cursor = db.things.find();
> cursor[5]
{ "_id" : ObjectId("4b33df5b844fab562308bb64"), "x" : 4, "j" : 4 }

Important note: mongoDB cursors are not snapshots. For instance, if your cursor has 10 documents, and another user removes one of them from the collection, your cursor will return only 9 documents. You have to use explicit locking to prevent this.

How to query data

Let’s see how to query the database for specific things we need to find.

The important thing to know is that queries, in a mongoDB database, are documents themselves. Let’s have a look:

SELECT * FROM things WHERE model=’Corolla’;

will be:


> db.things.find(
     {model:'Corolla'}).forEach(
     function(x) { print (tojson(x));});
{
        "_id" : ObjectId("4b33dee3844fab562308bb5f"),
        "brand" : "Toyota",
        "model" : "Corolla"
}

Now, if we want to specify what fields we need, instead of running a “SELECT *” query, mongoDB lets you return “partial documents”. To do this, you supply a second argument to the find() method, specifying what elements you need it to return:

SELECT brand FROM things WHERE model=’Corolla’;


> db.things.find(
     {model:'Corolla'}, {brand:true}).forEach(
     function(x) { print (tojson(x));});
{ "_id" : ObjectId("4b33dee3844fab562308bb5f"), "brand" : "Toyota" }

As you see, the query only returned the field ‘brand’ along with the object ID.

So, how to limit how many documents the database will return? Use the limit() method:

SELECT j FROM things WHERE x=4 LIMIT 2;


> db.things.find({x:4}, {j:true}).limit(2).forEach(
     function(x) { print (tojson(x));});
{ "_id" : ObjectId("4b33df5b844fab562308bb61"), "j" : 1 }
{ "_id" : ObjectId("4b33df5b844fab562308bb62"), "j" : 2 }

Or you can use the helper method findOne, if you want to return only 1 document:


> db.things.findOne({x:4})

{ "_id" : ObjectId("4b33de25ba7e40276b83ad53"), "x" : 4, "j" : 1 }

Note that in this case, all fields will be returned.

Conclusion

mongoDB is easy to learn. Its features are useful for web apps, specially having the need to build something scalable right from the beginning. Being a document database, it provides greater flexibility never before offered by traditional databases. mongoDB and its friends like CouchDB are definitely worth a look.

In the next article we’ll see how to start playing with mongoDB in Rails. Stay tuned! :)

Portraits and Photography

September 27th, 2009

Clique aqui para a versão em Português.

My latest passion has been portraiture and photography in general._dsc0600-flickr

From an old hobby, the interest only grew even more after I discovered the Strobist website. David Hobby teaches everything about lighting your little flash unit, and the secret is: use it off your camera.

A photographer named Jeremy Cowart created a movement called Help Portrait, check the video here and the Help Portrait website. On December 12th photographers from around the world will go out in the streets and reach people in need, and simply take their photographs. This is to help people that would never been able to pay for a photograph. The idea is: shoot and deliver the prints later. Just that. Connections will be made and you’ll learn a lot about people.

Inspired by a post on Zack Arias blog, I started to take some portraits right away. This is in preparation for the Help Portrait official day. So I went ahead and asked if a local retirement home would open their doors so I could photograph (for free) all of their 60 residents, elderly ladies and men. They were very happy to get this offer and yesterday I could do the photo session.

I’m really happy with the results. Many of the ladies and men were very happy about the photos. Some were not excited about having a photo taken, so for these I’d just showed some of the previous photos and let them decide. After this, some got excited and let me take their photos. Soon I’ll be able to see their joy when I deliver them the prints.

I started to process some of the images, and the best ones I posted on Flickr. I’ll be printing all photos and giving to them.

I learned that from very small things we can make people in need a little more happier. I saw a phrase written on their car that read “Love never gets old”.

Check some of the elderly portraits I shot on the retirement home.


Minha recente paixão tem sido retratos e fotografia em geral.

A partir de um hobby antigo, o interesse cresceu ainda mais depois que descobri o site Strobist. David Hobby ensina tudo sobre como iluminação com flashs portáteis, e no final das contas o segredo é: use ele fora de sua câmera.

Um fotógrafo chamado Jeremy Cowart criou um movimento, o Help Portrait. Confira o vídeo aqui e o site do Help Portrait. Em 12 de dezembro fotógrafos do mundo inteiro vão sair nas ruas, encontrar pessoas com algum tipo de necessidade (seja pessoas sem-teto, crianças órfãs, ou qualquer tipo de necessidade), e simplesmente vão dar um presente: tirar uma fotografia delas e depois levar a foto impressa. Isto será feito para ajudar aqueles que talvez nunca poderiam pagar por uma fotografia. Conexões serão feitas e você vai aprender bastante coisas sobre pessoas.

Inspirado por um post no blog de Zack Arias, comecei a tirar alguns retratos imediatamente. Esta é uma preparação para o dia oficial do Help Portrait. Fui em frente e perguntei em uma casa de repouso da cidade, se eu poderia visitá-los e tirar fotos (gratuitas) de todos seus 60 residentes. Eles ficaram muito felizes com a oferta e ontem eu pude fazer a sessão de fotos.

Estou muito feliz com os resultados. Muitos das senhoras e senhores ficaram felizes com a idéia de tirar fotos. Alguns não ficaram muito animados com a idéia, mas nestes casos eu mostrava as fotos de outras senhoras e alguns se animavam depois disso. Em breve vou poder ver a alegria deles ao receberem as fotos em mãos.

Comecei a processar algumas das imagens, e postei algumas das melhores no Flickr. Vou imprimir todas as fotos e dar de presente para eles.

Aprendi que com pequenas coisas você pode fazer pessoas em necessidades um pouco mais felizes. Vi uma frase escrita em um carro deles escrita assim “O amor nunca envelhece”.

Confira alguns dos retratos das senhoras da casa de repouso.

Palestra: Web Performance Client Side

September 14th, 2009

215987893_f665d2fa1dHá algumas semanas tive a oportunidade de palestrar internamente em dois eventos aqui na Locaweb:

  • Locaweb Tech Day: um evento de dia inteiro que ocorre a cada 2 meses, onde cada pessoa pode palestrar sobre um assunto de interesse;
  • Palestras Técnicas: ocorre às quintas-feiras, durando 1 hora. Uma pessoa por semana palestra sobre um assunto técnico.

Nas duas oportunidades palestrei sobre como fazer um site ficar mais rápido, e abrir num “estalo”. O título das palestras foi: Web Performance Client Side.

Se você já parou pra pensar em otimização de um website, vai perceber que o lado server side (sua aplicação Rails, PHP, etc) conta na velocidade de carregamento, mas na prática representa muito pouco. Isto acontece porque uma requisição é processada pelo servidor em 200 milisegundos, digamos, enquanto o mesmo site pode levar 10 segundos para carregar no browser. Então qualquer otimização que ocorra em server-side só vai afetar a quantidade de pessoas que podem acessar seu site ao mesmo tempo. Não afeta a velocidade individual que cada pessoa vai ter ao navegar nas páginas de seu site.

E para melhorar a velocidade client-side, a que realmente importa na maioria das vezes, existem várias técnicas inteligentes e interessantes. Na apresentação que coloquei no SlideShare, você pode pegar um apanhado com as últimas técnicas para desenvolvimento web com vistas à performance no browser.

Existe muita coisa mesmo na apresentação, e é imperdível pra quem trabalha com Desenvolvimento Web.

Veja a apresentação sobre Web Performance Client Side no SlideShare. Ou baixe o PDF, mas para isso você precisa ter conta no SlideShare.

Quick and dirty way to find broken links on your website

July 23rd, 2009

These days I had to find if there was any broken link (error 404) in a group of sites.

I found this to be very useful:

wget –recursive –spider http://levycarneiro.com

This command will download everything from the URL, and generate a report like this:

Found 13 broken links.

http://levycarneiro.com/levy@levycarneiro.com referred by:
http://levycarneiro.com/
http://levycarneiro.com/images/posts/Multiple_models_one_form_NewProject.jpg referred by:
http://levycarneiro.com/category/ruby-on-rails/
http://levycarneiro.com/tag/twitter/levy@levycarneiro.com referred by:
http://levycarneiro.com/tag/twitter/
http://levycarneiro.com/tag/rails/levy@levycarneiro.com referred by:
http://levycarneiro.com/tag/rails/
http://levycarneiro.com/tag/portfolio/levy@levycarneiro.com referred by:
http://levycarneiro.com/tag/portfolio/
http://levycarneiro.com/2009/04/transito-nao-an-experiment-with-twitter-traffic-jams-and-ruby-on-rails/levy@levycarneiro.com referred by:
http://levycarneiro.com/2009/04/transito-nao-an-experiment-with-twitter-traffic-jams-and-ruby-on-rails/
http://levycarneiro.com/tag/traffic/levy@levycarneiro.com referred by:
http://levycarneiro.com/tag/traffic/
http://levycarneiro.com/tag/projects/levy@levycarneiro.com referred by:
http://levycarneiro.com/tag/projects/
http://levycarneiro.com/category/projects/levy@levycarneiro.com referred by:
http://levycarneiro.com/category/projects/
http://levycarneiro.com/tag/ruby/levy@levycarneiro.com referred by:
http://levycarneiro.com/tag/ruby/
http://levycarneiro.com/category/ruby-on-rails/levy@levycarneiro.com referred by:
http://levycarneiro.com/category/ruby-on-rails/
http://levycarneiro.com/category/twitter/levy@levycarneiro.com referred by:
http://levycarneiro.com/category/twitter/
http://levycarneiro.com/tag/ruby-on-rails/levy@levycarneiro.com referred by:
http://levycarneiro.com/tag/ruby-on-rails/

I’ve got some work to do then :)

Google Chrome prefetches DNS for speed up browser performance

June 29th, 2009
Google Chrome

Google Chrome

Browser performance problems? Simple tricks to the rescue:

“To speed up browsing, Google Chrome resolves domain names before the user navigates, typically while the user is viewing a web page. This is done using your computer’s normal DNS resolution mechanism; no connection to Google is used. As a result, user navigation time in Google Chrome when first visiting a domain is on average about 250ms faster than traditional browsing, and the occasional but painful 1-second-plus delays are almost never experienced.”

And how it works:

DNS prefetching just resolves domain names before a user tries to navigate, so that there will be no effective user delay due to DNS resolution. The most obvious example where prefetching can help is when a user is looking at a page with many links to various unexplored domains, such as a search results page. Google Chrome automatically scans the content of each rendered page looking for links, extracting the domain name from each link, and resolving each domain to an IP address. All this work is done in parallel with the user’s reading of the page, hardly using any CPU power. When a user clicks on any of these pre-resolved names to visit a new domain, they will save an average of over 250ms in their navigation.”

Check the full article.

Very clever and efficient.

Making the web faster

June 26th, 2009

It was about time!

Google has announced an initiative to help developers to make their applications and websites to run faster. Nothing better than Google to push this kind of movement.

Let’s make the web faster is the main page, filled with increasing number of articles and videos on page speed optimization. Check it out!

I’m looking for ways to setup an automated script that will run YSLOW and PageSpeed tests on any given site. The idea is to monitor a site’s performance over time. You work hard to reach something around 90/95 on these test reports, and you don’t want other developers in the team (or yourself) to mess things up one month ahead, when you completely forgot about page’s performance :) So, if you know something about this, please let me know. Thanks!

Melhorando a performance de um site no browser

June 26th, 2009

Postei um artigo no blog de Tecnologia da Locaweb. Confira abaixo o artigo na íntegra.

Melhorando a performance de um site no browser” (client-side).

Escalar um site para atender milhões de usuários é sempre bom. Mas isto raramente é o problema. O mais importante é melhorar o tempo de carregamento ou renderização de uma página no browser. Aqui os segundos fazem diferença entre um site que abre “num estalo” e outro que demora a carregar acabando por frustrar o visitante.

Para melhorar a performance client-side de seu site existem diversos itens que você precisa aperfeiçoar em seu site:

- Reduzir o número de componentes na página. Uma página com 30 imagens, 10 arquivos CSS e 15 arquivos Javascript realmente vai demorar um certo tempo para ser carregada no browser. É possível reduzir isto tudo para, digamos, 2 imagens, 1 arquivo CSS e 1 arquivo Javascript, usando CSS Sprites e técnicas de consolidação de arquivos. Agora sim seu site carregará muito mais rápido;

- Usar técnicas de cache client-side. Arquivos que não mudam tão frequentemente como o logotipo de seu site, alguns ícones, e até arquivos CSS e Javascript podem usar o cabeçalho HTTP “Expires” fazendo com que browsers mantenham estes arquivos em cache local, afinal pra que baixar os mesmos arquivos toda vez que um visitante abre seu site? Para isto funcionar direito, você precisará expirar os arquivos atualizados com um número sequencial, por exemplo: logotipo.jpg?20090622001 (ano/mês/dia/alteração_do_dia).

Estes não são os itens mais importantes e nem os únicos. Para ver uma lista completa de coisas que influenciam na velocidade de seu site, baixe o plugin YSlow no Firefox, e rode o teste de velocidade em seu site. Quanto maior a nota, mais rápido é seu site. No relatório existem explicações sobre o motivo daquele item impactar na velocidade de seu site, e o mais importante: como fazer para melhorar.

Um plugin semelhante e bem recente é o Page Speed da Google. Um ponto forte dele é conter diversas ferramentas que ajudam na otimização de seu site, dentre elas, uma excelente que gera online uma versão otimizada das imagens, permitindo que você possa usá-las no site imediatamente. Por exemplo: se uma imagem banner.jpg contém 100KB, você pode gerar uma versão otimizada com apenas 30KB (depende de imagem para imagem), economizando em transferência de dados e velocidade de carregamento.

Um blog interessante que fala sobre este tópico, é o blog de Steve Souders, autor do livro High Performance Web Sites. Tanto o blog como o livro são extremamente recomendados.

Hidden Costs of Scaling Up vs. Scaling Out

June 25th, 2009

The well-known dilemma: scale vertically (buy hardware) or scale horizontally (add machines)?

Here’s an interesting point of view from an article at Coding Horror entitled

It’s fair to conclude that scaling out is only frictionless when you use open source software. Otherwise, you’re in a bit of a conundrum: scaling up means paying less for licenses and a lot more for hardware, while scaling out means paying less for the hardware, and a whole lot more for licenses.

All in all I still prefer scaling out, so you don’t have a single point of failure.