Web Framework Manifesto A quick and easy way to map between a relational database and the target application. Rails' ActiveRecord is a really great example of how to map a relational database. Sure, you might want to use Magma with Squeak for true object persistence, but for most applications in most of the world, you've got a relational database on the back end and you've gotta map to it. The mapping should “do the right thing by default” and all the schema and class information should live in 1 or at most 2 (e.g., migrations and model class) places. Easy, “right by default,” HTTP request mapping. A request comes in and gets routed to the right place. This is another place that Rails really shines (at least at the “page” level.) PHP and JSP also do well here. Schemes (like Struts) that require 35 configuration files, etc. to say “/foo/bar/33” gets routed to the Bar method on the Foo controller are way to complex. Seaside takes this to yet a better level... requests get mapped to the right closure in the right component. But I digress. Automatic “view” selection and composition. Basically, the “right by default” view should be selected based on the request, but alternate views should be specified by the “controller.” Views should be CSS friendly. I'm split over having separate template files (e.g., Rails, JSP, etc.) or embedding the HTML in the file (e.g., Seaside) or having both options (Erlyweb.) Pages must be composed of arbitrary components that manage their own state. This means that the search panel, the scrolling “what's hot” area, the catalog, and the shopping cart are all separate components. Seaside really excels in this area. Check out the Seaside Sushi store demo that demonstrates many different components with different state all on the same page. Remember also, that the component nature of the page means that the components each receive their own UI messages. This is a place where Rails does not excel. The rendering of components must be asynchronous based on user-based and external event-based state change. This means that if state changes in the component, the UI should be updated. Maybe it's updated the next time the page is reloaded (this is the way Seaside's Sushi store works,) the next time there's an AJAX request made, or via a Comet push. The components should be agnostic to the update mechanism. They should merely mark themselves as dirty and be re-rendered the next time there's an opportunity. Components should be live (or seamlessly persisted) at all times, ready to respond to events. The browser should be honored and feared. That means the back button should “do the right thing” (see Seaside) and input from the browser should never be trusted, but should always be tested, validated, and destroyed if it is unexpected (e.g., throw away a POST that contains parameters that were not in the form presented to the user.) There should be a single way of describing input validation. That validation should happen whenever possible on the client, but should always be repeated on the server and before any model state is modified. Mapping between object fields and HTML (or whatever the presentation layer is) should be “right by default” and should be extensible based on new technology. Rails and view helpers rule here. There should exist an orthogonal security layer such that objects that are not accessible to a user should never be returned in a query for the user and fields on an object that are not accessible should not be visible. The security and access control rules should be algebraic and demonstrable during a security audit. This means that neither the view nor the controller should have to test for access control. Objects and requests should be sanitized before they get to the “controller.” Code should be impervious to a replay attack. That means that fields in forms should have random names that change for each request. There should exist a simple, unified way to describe modal user behavior (e.g., filling out a multi-page form.) Seaside rules in this respect. Sessions should be tied to a browser window/tab, not to a browser session. Once again, Seaside really rules on this count. The framework and runtime should correctly and gracefully deal with non-ASCII characters. Deploying the web application should be as simple as putting a file in a known location (e.g., a WAR file on a J2EE server) or by executing a single command (e.g., Capistrano.) Deployments should contain all dependencies such that as long as the target system meets a particular minimum specification (e.g., running Java 1.4 and Tomcat 5.5), the application will work without having to load other configuration files. Deployment and management should be able to be done via command line or a web browser and should never require VNC or some other screen-cast or screen scraping. Testing should be an integral part of the framework and should allow simulating HTTP requests. Rails has the best testing framework of any web development framework I've seen. The production environment should support modern technology including executing multiple threads in a single process and allowing for many “live” objects to be corresident (an absolute necessity for Comet-style applications.) The production environment should support hot code replacement such that new code can be placed in production without impairing existing user state. The development environment should support hot code replacement such that once a file is saved, it becomes live at the next HTTP request. Sure, it may be compiled and moved to the app server on save (Eclipse does this with Java code) but the developer should not have to explicitly compile, restart, reload in order to test a change. The system should be able to map input from a variety of different formats (SOAP, REST, SMTP, etc.) such that requests are normalized and responses are sent over the appropriate medium. There should exist a mechanism for adding functionality to the system with few or no API calls. Rails Engines are an example of this. Subsystems and added functionality should be defined by a clear interface that can be tested and validated during a compile or test cycle. Using parts of the subsystems that are not defined by the interface should be flagged during the test or compile cycle.