|
Peter Marklund's Home |
Peter on Rails
Lessons Learned in Ruby on Rails and related technologies.
Subscribe
New Rails Plugin for HTML Validation: Html Test
Update on 2009-04-16: The source code has been moved from Google Code to GitHub
Update on 2007-12-12: I have renamed the plugin from "Http Test" to "Html Test" and moved it to a new home at Google Code. Sorry about the inconvenience that this may cause those of you who have the plugin installed and want to update it. The change is essentially a namespace change from Http:: to Html:: so it should be manageable. Thanks.
As I wrote earlier I like to monitor my production sites with HTTP tests that validate their HTML and check for broken links and images. I think that serves as a good baseline check, a smoke test if you like, that the site is not fundamentally broken.
It used to be that my HTTP tests were implemented in Perl using WWW::Mechanize. Now that we have the wonderful Ruby and Ruby on Rails at our fingertips that felt like a real shame. I decided to implement my HTTP tests in Ruby and once I had gotten that far, going the extra mile to package my scripts as a Rails plugin seemed natural. The plugin is called http_test and it's available at Google Code.
Hype and the Rise and Fall of Programming Languages
Steve Yegge has written an article on Ruby as a competitor to Python, the Rails hype, and the rise and fall of programming languages. The article touched me deeply. Thanks to Olle Jonsson for sending the link. The article is almost a year old but should still be relevant. I especially like the last section on Ruby:
"The worldwide Ruby culture is the warmest and friendliest I've seen in my long history with programming languages. And Ruby is a sweet language. Other people seem to agree, and are taking steps to market it, which is getting them labeled as "hyper-enthusiasts" by the Sour Grapes camp. It appears to me that Ruby is doing what I wanted Python to do a few years ago, so I've finally learned Ruby and have switched most of my development over to it.
MySQL Gotchas and a Deadly Sin
Coming from mostly a PostgreSQL and Oracle background I experienced some major friction working with MySQL today.
The first obstacle to overcome was the syntax for foreign keys. I started out using
create table test (
service_id integer NOT NULL references services(id) on delete cascade
);
Usability on the Web and Embracing Simplicity
I just booked flight tickets for the christmas holiday and it got me thinking of usability. People depend on websites so much these days and it's a shame to let them suffer from poor usability. Of course, usability has improved quite a bit over the last couple of years. Indeed SEB finally uses a date widget, and I think even suggests a sensible default (the next working day), when you do payments through their online bank. For the longest time they left the user with a bare bones YYYYMMDD text input widget.
Booking tickets on the web is often a series of frustrations for me. This is in spite of the fact that I am a skilled web user. For example a form won't submit and the validation error is hardly visible. The user is left wondering if the form submitted at all, if there was a validation error, and if so, where?
Third Place in Salsa Competition
There is a quote by Woody Allen that rings more and more true to me:
Eighty percent of success is showing up
Last friday me and my girlfriend Janne were able to win third price at a Salsa competition at La Isla here in Stockholm. Thanks to the encouragement of Pilar and Ociel at Stockholm Salsa dance we decided to give the competition a try and it was great fun! My friends Håkan and Mimmis won the silver, and a very professional and choreographed couple from Uppsala took home the gold. We were very pleased to walk home with the trophies and 1000 SEK though...
There is a huge difference between social dancing and show dancing, and as my friend Håkan said, if you want to be a successful show dancer you need to learn to dance outwards (with the audience) rather than inwards (with yourself and your partner and the music).
Teaching Rails at Great Works
I've started teaching Ruby on Rails at Great Works and I'm very excited about it! It's a three evening course given to a small group of up to 10 people. We use a lot of hands on excercise rather than lecturing and the goal is to build a small Rails application. The course participants are designers rather than programmers so we needed to lay down some object orientation and programming basics before we could take on Rails. Despite of this obstacle we were able to make a lot of progress by using Locomotive for installation and scaffolding for UI generation. You can check out the slides here.
I genuinely enjoy teaching and am now looking for more opportunities to help companies learn and get started with Rails quickly. Get in touch if this sounds interesting.
Stockholm Rails Meetup Tonight was Awesome
We had a fantastic turnout and great atmosphere at the Rails Meetup at Valtech here in Stockholm tonight. There was a great presentation by Ola Bini on web services and Rails, and one by Christian and Albert at Adocca about caching and a bunch of plugins that they have realeased at RubyForge. All very exciting.
I gave a presentation on the Media Contact Manager system that I have built for Green Media Toolshed.
Rails: File Uploading and Storage
File uploading and storage is no big deal, right? I mean with Ruby it can be as easy as:
def create
File.open("/Users/db/_dev/rails/videos/swing.mov", "w") do |f|
f.write(@params["video"].read)
end
end
Don't forget to set :multipart => true on your form though. The easiest (and most naive?) approach to uploading files is probably to use the File Column plugin. The Rails Wiki has a good page that discusses file uploads in more depth. The file system base storage approach that I've used in my Rails app is similar to what's described here. I use the after_save and after_destroy callbacks to create and delete the file in the file system - an approach also described in this Rails manual. Johan Sorensen has some code for doing buffered file uploads that might be useful for huge files.
This page is quite impressive. It has everything you ever wanted to know about the input type="file" form widget, including why browsers will ignore the value attribute if you try to set it. An annoyance is that if the user gets validation errors on a form containing a file_field widget she will have to re-select the file to upload.
The classic controversy is whether to store files in the database or in the filesystem. If you store them in the database you have a more unified and consistent storage solution. You keep all your data in one place, in one format, and the only data you need to back up is your database. On the other hand, putting files in the file system could potentially offload your database and make the database dumps smaller. Also, databases were not really intended to store files, that's what file systems are for. Personally I'm ambivalent on this issue, but due to some incidents with file system storage recently I'm now leaning more in favor of storing files in the database.
Rails script/console - Did you know it could do all this?
Yesterday someone pointed out some features of the Rails console that I had missed out on. Here are some objects and commands to be aware of:
- reload! - reloads the application with your model classes. Useful when you've made changes to your code
-
app - an ActionController::Integration::Session object that lets you run through whole integration tests from the console. Use app(true) to create a new session. Here is a sample of commands:
- app.get("/")
- app.assert_response :success
- app.response
- app.request
- app.assert_tag
- app.flash
- app.assigns
- app.assert_template
-
helper - an object with all the ActionView::Helpers::* modules mixed in. Methods that you can experiment with include: link_to, url_for, pluralize, form_tag, date_select, link_to_remote. If you would like to experiment with your application helpers you can do this:
>> class MyHelper >> include ApplicationHelper >> include all the helpers you wish to test here... >> end => MyHelper >> my_helper = MyHelper.new => #<MyHelper:0x25e2710> >> (my_helper.methods - Object.methods) => list of helper methods to play with follows...
Rails Patch: Rake :test should not fail silently
Swallowing exceptions and failing silently has to be one of the worst sins you can commit as a programmer and I was surprised to run into that issue with the Rake :test task. The Rake :test task (the default task) will in turn invoke the tasks test:units, test:functionals, and test:integration. Any exceptions raised within these sub tasks are not exposed directly but are merely noted as an overall test failure - obscuring the root cause of the failure. This is obviously not very debugging friendly if you have an exception raised somewhere deep down in the Rake task dependency hierarchy. I submitted a patch to remedy the problem.
Rails QA: Watch Out for Duplication in your Fixtures
I had a long debugging session due to duplicated record keys in one of my fixture files. It turns out the YAML parser will not complain if you have duplicated record keys (the keys on the top level) or duplicated column keys. Eventhough the YAML specification says that map keys should be unique the YAML parser will happily overwrite any existing value for a key if it encounters that key again.
Under the motto of crashing early and avoiding silent failures I came up with the following unit test:
Rails Recipe: Checking for Broken Links in Integration Tests
Having a way to automatically check for broken links in your application can be quite valuable and there is a simple way to do that with integration tests. Here is the DSL methods that I used for that purpose along with some sample usage:
Rails Workaround: Preserving Nested Hash Params With List Values Across Requests
Suppose you have an HTML search form with a multi-valued select with the name "person[personal][interests][]". The trailing brackets are there to indicate to rails that this HTTP parameter should be converted to a Ruby list. Now, suppose the form submits to the search action in the contacts controller. Our params hash will then contain:
params = {
:controller => 'contacts',
:action => 'search',
:person => {
:personal => {
:interests => ['music', 'chess']
}
}
}
Rails Recipe: HTML Validation
In this howto I'll show a simple approach to HTML validation that I use in my current Rails application. For me, HTML validation is a way to achieve wide browser compatibility, and to do a baseline check for correct rendering and UI brokenness. By putting ampersands and less-than signs in my test fixtures I can use HTML validation tests to check that I haven't forgotten any HTML quoting in my templates, i.e. that I haven't forgotten to use the following constructs:
<%=h some_variable %> <%= link_to h(some_variable) ... %>
Rails Custom Validation
Suppose you have validation code for a certain type of data that you want to reuse across your ActiveRecord models. What is the best approach to doing this in Rails? As you probably know, Rails comes with a number of validation macros such as validates_presence_of, validates_format_of, and validates_uniqueness_of etc. One approach is to write your own validation macro that wraps one of the Rails validation macros. The validates_as_email plugin shows us how:
module ActiveRecord
module Validations
module ClassMethods
def validates_as_email(*attr_names)
configuration = {
:message => 'is an invalid email',
:with => RFC822::EmailAddress,
:allow_nil => true }
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
validates_format_of attr_names, configuration
end
end
end
end
Rails Recipe: A Timezone Aware Datetime Picker
NOTE! Only follow this recipe if you are using an earlier version of Rails than 2.1. As of Rails 2.1 timezone support is built into Rails in a way that makes it much easier to create timezone aware applications.
Quite often in web applications we display dates and times and also have users input them and store them in our databases. If your users are spread across the globe you really want to be able display times in the users own timezone. How do we accomplish this with Rails?
First off, as Scott Baron has pointed out, you want to grab a copy of the TZInfo Ruby timezone library. We need TZInfo since it can deal with daylight savings time (summer/winter hourly adjustments), something that the Timezone library that ships with Rails is not able to do. Installation is as simple as downloading the latest tgz file from RubyForge, extracting it into vendor/tzinfo, and requiring the library from config/environment.rb. We also add some new settings:
Rails Recipe: CSV Export
A common requirement from customers is the ability to export tabular data to a CSV file that can be imported into Excel. Ruby on Rails uses the standard Ruby CSV library to import test fixtures that are in CSV format. However, there is a runner up CSV library called FasterCSV which as the name suggests is faster and also has a much cleaner API. I was hesitant to use FasterCSV in production since it is still in Beta but after finding the discussion about including FasterCSV in the Ruby standard library I decided to give it a try.
This Might Cost You a Little...
I just received an invoice from my dentist for 900 SEK (about a 100 EUR) for some unspecified service. I called them and it turned out I was paying 900 SEK for being 15 minutes late. What happened was I over slept and four minutes into the appointment when I was rushing out of my apartment to get on my bike my dentist calls (from Folktandvården, Eastmaninstitutet). I apologize and tell her I can be at her clinic in 7 minutes on my bike. She says nah, I'm not sure it's worth you coming in now that you're late, we better schedule a new time for you. I say ok, given that I didn't seem to have a choice. We book a new time and then at the end of the conversation she mentions that "oh, this might cost you a little" (the exact words in swedish were "det här kommer kanske att kosta lite").
Interesting points of comparison here is that once before when I missed an apointment I paid 300 SEK. My mom says she has never had to pay for missed appointments at her dentist. Last time I visited the dentist and they examined my teeth I paid 500 SEK, which right now seems pretty cheap, especially given that that time they actually performed a service for me.
The Weepies Make me Weep with Joy
I just came home from a concert with The Weepies (a folk/pop duo from the US) at Debaser here in an exceptionally warm and sunny Stockholm.
The Weepies made me all misty eyed, showering beautiful harmonies of vocals and guitars over a small audience in an intimate concert setting. I loved the fact that about half of the songs were new to me since they were drawn from the solo CDs of Deb Talan and Steve Tannen that preceeded The Weepies. They also played a new song about an "Old Coyote" to be released soon on Itunes...
Rails Code Generation Considered Useful
I just checked out Ryan Daigle's excellent What's New in Rails blog. There are a lot of great findings there for anyone like me who hasn't monitored closely the changes to the Ruby on Rails source code. In the post about the observer generator Ryan writes:
"On a side note, I feel like the generator utility has been getting a bad rap recently from people that frown upon auto-generation of code. Fair enough, but think of them more as best practices templates and learning tools rather than some sort of coding robot. They're really great tools for seeing how things should be done, and how things are expected to be done. If you don't want to use them, don't - but if you're not quite sure how to go about creating an observer, or controller, or model they're of great help."
Ryan puts it very well and I couldn't agree more. I've found the scaffold generator very useful in the project I'm working on now, and it's not for prototyping I'm using it, but for providing a skeleton for the production code I'm going to write.
Another advantage of the code generation in Rails is that if you use it your code will probably follow conventions (naming of controller actions for example) more closely and this will make the code easier to read and maintain for other developers. The generated code also contains best practices (such as the verify :method => :post for certain actions in controllers) that an average Rails programmer (or even a good Rails programmer) may not know about or may forget to use otherwise.
Presentations from the Rails Recipes Meetup
The Rails Recipes Meetup yesterday was a great success with over 20 participants and four interesting presentations. I would like to thank Xlent for hosting us and all the people who showed up and contributed to making the evening so nice.
If you are interested in coming to the next meetup (scheduled to happen in June or July), please signup for the swedish Rails mailing list at rails.se. Please also suggest presentation topics on that mailing list.
Here are two of the presentations from the meetup:
- Test Driven Development with Ruby by Peter Marklund
- Scoping ActiveRecord by Marcus Ahnve
See you at the next meetup!
Ruby on Rails Tips and Tricks
Starting a Startup - Hard but Doable
Here are some encouraging words from Paul Graham's How to Start a Startup article (which will make you scroll through eternity):
"You need three things to create a successful startup: to start with good people, to make something customers actually want, and to spend as little money as possible. Most startups that fail do it because they fail at one of these. A startup that does all three will probably succeed.
And that's kind of exciting, when you think about it, because all three are doable. Hard, but doable. And since a startup that succeeds ordinarily makes its founders rich, that implies getting rich is doable too. Hard, but doable."
Interesting to see the Web 2.0 Startup Toplist as well for a list of successful startups.
From Paul Graham's Y Combinator:
"Y Combinator relies on certain premises: that open-source software and falling hardware prices means that tech start-ups are cheap to finance; that large companies are no longer at the forefront of innovation; and that mature technology companies find it cheaper to buy than to build."
Rails Recipes Meetup in Stockholm
- What it is: A free and informal meeting for people interested in Ruby on Rails. An opportunity to network and share experiences.
- Date and time: Tuesday the 9th of May at 18:30
- Place: XLENT, Regeringsgatan 67, third floor - map. We will be in a conference room with a projector and room for 20 people. Bring a laptop if you can.
- Signup: Please signup by sending an email to peter_marklund AT fastmail dot fm. The number of seats is limited to 20.
- Agenda:
- 18:30-18:45 Participants present themselves
- 18:45-19:15 Presentation 1 - Test Driven Development - Peter Marklund
- 19:15-19:45 Presentation 2 - DRY up your ActiveRecord Code with Scoping - Marcus Ahnve
- 19:45-20:15 Break with coffee/tea and cake
- 20:15-20:45 Presentation 3 - Ajax Scaffolding - Nic Williams
- 20:45-21:15 Presentation 4 - Extending Rails with Modules and Plugins - Martin Kihlgren
- 21:15-22:00 Concluding discussion
- Participants:
- Andre Ekespong - Xlent
- Peter Marklund - freelancer
- Marcus Ahnve - Valtech
- Peter Krantz (unconfirmed) - Valtech
- Nic Williams - Tele2
- Martin Kihlgren - Adocca
- Teddy Zetterlund - Adocca
- Peter Bengtson (unconfirmed) - Adocca
- Tomas Jogin - Comigo (built Avinet.se)
- Joel Junström (unconfirmed) - Winston Design
- Jan Andersson (unconfirmed) - Gear
- Organizers:
- Andre Ekespong - Xlent
- Peter Marklund - freelancer
- Presentation Guideline:
Each presentation is up to 20 minutes long followed by a 10 minute discussion. The presentations should be in the Rails tradition of "show don't tell". We prefer short presentations with code samples from recipes applied to real problems over the traditional type lengthy and theoretical Powerpoint presentations.
- Reading tip: Rails Recipes by Chad Fowler. Here is what Mike Clark says about the book:
"Now, I know Rails well enough that over time I may have stumbled on some of the solutions, but that's not the point. With a collection of great recipes at hand - In-Place Form Editing, Live Search, RJS Templates, Polymorphic Associations, Tagging, Syndicate Your Site with RSS, and Sending Email with Attachments to name a few - I can quickly give my app the functionality today's web users have come to expect, then move on to the features that really set it apart from the crowd."
- Possible Discussion Points
- The web 2.0 frontend - Ajax, RJS, and Javascript. How do we best build fast, user friendly, and yet accessible UIs?
- Deployment. Choosing OS, database server, and web server. Experience with hosting companies. What are the main scalability issues? Caching strategies - using memcached etc.
- Reusing code across projects and websites - plugins, engines, components, and what have you. How do you choose between these techniques? How do they work?
- Human oriented programming. How concise should we make our code? How much should happen on a single line? When should we break lines up and use temporary variables? What are the characteristics of beautiful code?
- Using the right tool for the job. For what tasks is Ruby better suited than say Bash, Perl, Python, or Java?
- How much automated testing is worthwhile? How do we know when testing doesn't pay off? Are there certain types of programs for which automated testing doesn't make sense? How much test coverage should we strive for? How do we measure test coverage?
- Which Rails Recipes have people used? What about versioning, tagging, and migrations?
Quote of the Day
Thanks Jarkko for highlighting the following nice quote from the article Two more reasons why so many tech docs suck which argues the case of getting your users excited and using use cases for technical documentation:
One of the best techniques for creating and keeping interest is to make non-reference docs use-case driven. That way, each topic is framed by a context that matches something the user really wants to do. That way, each little sub-topic shows up just-in-time, instead of appearing to be there just-in-case.
It makes a lot of sense to me to organize technical documentation by the readers goals, i.e. to use the howto or cookbook approach. The Perl Cookbook is a great example of how this approach can be successful. In that instance the use-cases are on a very low level such as "How do I iterate over an array" but the approach should also work on a higher level, i.e. "How do I setup a weblog". High level use-cases like that are probably best broken down into smaller use-cases.
Quote of the Day
I was catching up with a lot of Rails related posts in my RSS reader and found DHH's bashing of James McGovern which was pretty entertaining. I also like this quote in the comments:
First they ignore you Then they laugh at you Then they compete with you Then you win
I also learned today that Dreamhost upgraded to Rails 1.1. Apparently my homepage survived that upgrade. I still don't have Rails in the vendor directory but I do have web tests set up so I don't have to worry too much.
Rails Meetup @ Akkurat
Tonight a bunch of hardcore geeks here in Stockholm met up at the excellent Akkurat pub to chat about Ruby and Rails. More than Rails though, the conversation seemed to drift towards ABC 80, Atari, and Amiga nostalgia which was great fun. I am very positively surprised by the atmosphere and service at Akkurat by the way and I now consider it to be my favorite pub here in Stockholm. The next time I need to try their special "Heaven" and "Hell" beers.
I've thoroughly enjoyed both the Rails meetups that we've had so far. The first one was held at The Dancing Dingo, the second one tonight at Akkurat, and the third one we are planning to have in a couple of weeks at Saddle and Sabre. To signup for notifications about future meetups in Stockholm make sure to get on the swedish Rails mailing list which you'll find at rails.se.
Sunday Brunch with a Touch of Flair
I enjoyed an exotic brunch today with my girlfriend at the flairful Amaranten hotel here on Kugsholmen in Stockholm. As you can see, the hotel lobby and restaurant is stylish and has a nice ambience. The buffet is offered at 160 SEK and is high quality with a good selection of meat such as chicken wings, spare ribs, bacon, and a number of more exotic dishes that I don't even know the name of :-) Some of the Cajun specialties didn't particularly speak to our taste but all in all it was quite good. On the minus side was the fact that the place was almost empty and one of the waitresses was not so service minded.
The atmosphere was enhanced by a house Jazz band - a trio on bass, drums, and piano. They performed professionally albeit somewhat uninspired, which is understandable given the lack of an engaged audience. We tried to cheer the band on by taking the initiative to applaud and I think this was appreciated.
I seem to have taken up an interest in luxury hotels lately - there is something fascinating about them. A couple of years ago I stayed with my girlfriend at the Grand hotel here in Stockholm - a very memorable and highly recommended experience. Recently I read in the paper about how the new Hotel Rival (owned by famed Benny Anderson of Abba) in Stockholm came in highest in scandinavia (at place ninety something) in a listing of the most luxurious hotels in the world. This sparked my interest and I have now booked a brunch there for the second of April and will spend the night with my girlfriend in one of their "de luxe" rooms. It's no exaggeration to say that I look forward to that...
Less is More - The 37Signals Mantra
I remember seeing a presentation by Jason Fried of 37Signals at Reboot in Copenhagen a couple of years ago. The presentation was about defensive web design and it was very professional and convincing. It seems now though, Jason has become not only an authority on web design but also on the general process of how to successfully build websites. I listened to Jason's interview at IT conversations a while back and it really impressed me. I felt like listening to the speech several times and write down all the important points that Jason is making. However, 37Signals beat me to that task and for only $19 you can have the Getting Real book which essentially covers and expands on the same philosophy and guidelines that are in the speech. The book notwithstanding, here are my own sketchy notes from the IT conversations interview:
Reduce mass. Physics tells us that the higher the mass of a moving object the more difficult it is to change its direction. Mass can be too many people, hardware, software lockin, and long term contracts. Ability to change is very important in any product development.
Embrace constraints. Examples of constraints are hardware, people, and money. Constraints is where creativity happens. Let people use the product and look for real patterns. Start with less and your more with be a lot more clear down the road. The initial release should be half the product. Everything is beta these days but its time to have confidence, public is public. Keep iterating and make what you have rock solid, bullet proof, and great
Getting real. Use few documents, specs and diagrams. Build top down - build the interface first.
Managing debt. Feel the hurt. The people who built the product should support it. Chefs become waiters once in a while.
Finding people. Look for people who are:
- Positive and excited
- Well rounded - able to do multiple things
- Quick learners
- Trustworthy. You need to be able to trust someone with a problem. You shouldn't have to cleanup after someone else.
- Good writers. Everybody on the team has to be a good writer
My First Acoustic Instrument
I just had to post a picture of my nice new LP Bongo drums:
It was in Copenhagen that I regularly started dancing salsa and through the dancing I developed an interest in the music. On the salsa mailing list in Copenhagen there came an invitation from Jakob Johansen to learn how to play in a salsa band. As a child I always wanted to play the drums but my dad wasn't happy about the idea of having drums in the house so that dream never materialized. However, in Copenhagen I jumped on the opportunity and by chance I ended up playing Timbales in the band. I was the only one in the band who was a total beginner but it worked out ok anyway and I throughly enjoyed it.
Once in Stockholm last year I was looking for the opportunity to continue playing in a salsa band. I found Pygge and Alternativa Musikskolan in Sollentuna where I took private lessons in Timbales. I decided to switch to conga drumming and pursued that for a while. Eventually I found a salsa band that I could play in. However, it turned out they alreay had a conga drummer so I had to learn the bongos instead. I find bongos easier to play than the congas and they are great for soloing. I think my new bongo drums look great - now I just have to learn how to play them...
Financial Freedom vs Liking What you Do
"Most people would say, I'd take that problem. Give me a million dollars and I'll figure out what to do. But it's harder than it looks. Constraints give your life shape. Remove them and most people have no idea what to do: look at what happens to those who win lotteries or inherit money. Much as everyone thinks they want financial security, the happiest people are not those who have it, but those who like what they do. So a plan that promises freedom at the expense of knowing what to do with it may not be as good as it seems."
Whichever route you take, expect a struggle. Finding work you love is very difficult. Most people fail. Even if you succeed, it's rare to be free to work on what you want till your thirties or forties. But if you have the destination in sight you'll be more likely to arrive at it. If you know you can love work, you're in the home stretch, and if you know what work you love, you're practically there.
Lessons from Adam Bosworth
Adam Bosworth's article Learning from THE WEB is well worth reading (thanks Branimir for pointing me to it). A surprising number of the lessons have to do with simplicity, for example the KISS one:
"KISS. Keep it (the design) simple and stupid. Complex systems tend to fail. They are hard to tune. They tend not to scale as well. They require smarter people to keep the wheels on the road. In short, they are a pain in the you-know-what. Conversely, simple systems tend to be easy to tune and debug and tend to fail less and scale better and are usually easier to operate. This isn't news. As I've argued before, spreadsheets and SQL and PHP all succeeded precisely because they are simple and stupid - and forgiving."
Other lessons such as "It is acceptable to be stale much of the time" and "The wisdom of crowds works amazingly well" ring very true to me. His critique of database vendors inability to learn from the web is thought provoking.


