The Way of the Code

The Zen of Python (by Tim Peters )

  • Beautiful is better than ugly.
  • Explicit is better than implicit.
  • Simple is better than complex.
  • Complex is better than complicated.
  • Flat is better than nested.
  • Sparse is better than dense.
  • Readability counts.
  • Special cases aren't special enough to break the rules.
  • Although practicality beats purity.
  • Errors should never pass silently.
  • Unless explicitly silenced.
  • In the face of ambiguity, refuse the temptation to guess.
  • There should be one-- and preferably only one --obvious way to do it.
  • Although that way may not be obvious at first unless you're Dutch.
  • Now is better than never.
  • Although never is often better than *right* now.
  • If the implementation is hard to explain, it's a bad idea.
  • If the implementation is easy to explain, it may be a good idea.
  • Namespaces are one honking great idea -- let's do more of those!

Design Principles

OO Design is more than just using an OO language. Over the years many bright programmers have built up a collection of rules that help to build well designed maintainable code. This article lists the main rules of OO programming. The intention is to inspire the reader to think about these rules and make further reading. There is a lot of material on the web that drills down into more details with plenty of examples.

Ivar Jacabson said ”All systems change during their life cycles. This must be borne in mind when developing systems expected to last longer than the first version”. In other words software requirements change with time. The goal of Object Orientated Design is to program in such a way that such changes to the software are predictable and do not make a large impact in the program. In other words it should be stable in the presence of change

Bad design is characterized by

  • A single change affects many other parts of the system (Rigidity)
  • A single change affects unexpected parts of the system (Fragility)
  • It is hard to reuse in another application (Immobility)

The Dependency Inversion Principle DIP

Imagine you have a simple database program. You don’t want to change the entire application when changing the database. This principle is targeted at removing such unwanted interdependency that can cause a design to be fragile. The rule states

  • High level modules should not depend upon low level modules. Both should depend upon abstractions
  • Abstractions should not depend upon details. Details should depend upon abstractions

Booch said “All well structured object orientated architectures have clearly-defined layers with each layer providing some coherent set of services through a well defined and controlled interface.”

In other words design applications in layers where high level layers call lower level layers using Abstract interfaces. To conform to the principle of dependency inversion, we must isolate abstraction from the details of the problem. Then we must direct the dependencies of the design upon the abstractions.

Good dependencies are extremely unlikely to change. In other words they are stable. We would like to base our architectural design around stable, non-volatile modules.

The Open-Close Principle OCP

Software entities (Classes, Modules, functions etc) should be open for extension, but closed for modification

In other words design classes that never change. When a new requirements come add new code and don’t edit existing code. It is not possible to close against all possible changes. Therefore an experienced developer needs to understand the possible future wishes of users in order to make Strategic Closure. There are two ways of closure:

  • Using Abstraction to gain explicit closure - This means the programmer applies abstraction to those parts of the programmer the designer feels are subject to change.
  • Using Data Driven Approach to achieve closure

Liskov Substitution Principle LSP

Every function that operates upon a reference or pointer to a base class should be able to operate on derivatives of that base class without knowing it. This means that virtual member functions of the derived class must expect only all the corresponding member functions of the base class. In other words any function that uses a base class must not be confused when a derived class is substituted for the base class

This is a difficult principle to apply. To conform avoid overwriting base class functions because this involves programming with details, instead try to program in abstractions

If this is violated then functions that operate on the pointers must first check the type of the actual object in order to work correctly.

Heuristics and Conventions

Make all member variables Private: Otherwise no function that calls the class can be closed to change. For example a status variable can change from Boolean to an enumeration, if this is not handled as a property then we cannot close status. This is called encapsulation.

No Global Variables.

Because misbehaving modules may write erroneous data to such global variables whose effect can be felt in many places throughout the program. Sometimes Global variables are useful e.g. cout and cin in c++. If they do not violate the open close principle then sometimes they are worth the style violation

Stability Dependencies Principle SDP

The dependencies between packages in a design should be in the direction of stability of the packages. A package should only depend upon packages that are more stable than it is.

Some volatility is necessary if the design is to be maintained. This is achieved by using the Common Closure Principle, in this way we design packages to be volatile and we expect them to change. Any package that we expect to be volatile should not be depended upon by a package that is difficult to change.

Some things we don’t want to change. For example architectural decisions should be stable and not at all volatile. Therefore classes that encapsulate the high level design should be stable.

The stable Abstractions Principle SAP

Packages that are maximally stable should be maximally abstract. Instable packages should be concrete. The abstraction of a package should be in proportion to it's stability

Common Reuse Principle CRP

If you reuse one class of a package, you reuse them all. This because any package delivered contains a released set of classes, therefore a change in any class means a new release of the entire package.

The Reuse / Release Equivalence Principle REP

The granule of reuse is the granule of release. Only components that are released through a tracking system can be effectively reused. This principle is important when there are several teams working on an application. To avoid one team disrupting another all packages used are tested and released. In this way the introduction of modified packages is in a controlled way.

The Common Closure Principle CCP

The classes in a package should be closed together against the same kinds of changes. Any change in a package affects all classes in the package. Just like a well organized team has a common goal because they all have to work together. This principle means that you should have a common strategic closure concept used through all classes in a package because they have to be released all together.

The stability Dependencies Principle SDP

The dependencies between packages in a design should be in the direction of stability of the packages. A package should only depend upon packages that are more stable than it is.

Designs that are highly interdependent tend to be rigid, not reusable and hard to maintain

The Acyclic Dependencies Principle (ADP)

The dependency structure between package must be a Direct Acyclic Graph (DAG). This means that if you plot out all packages it should be possible to arrange the dependencies to always point from top to bottom. Also it should not be possible to follow any lines of dependence and end up back at the same package. Because such packages would have to be released all at the same time defeating the object of having them as separate packages

The Interface Segregation Principle ISP

Clients should not be forced to depend upon interfaces that they don’t use.

This principle deals with the disadvantages of fat interfaces. Fat interfaces are not cohesive. In other words the interfaces of classes should be broken into groups of member functions. Each groups servers a different set of clients. Separation can be achieved by:

  • Separation through Delegation
  • Separation through multiple inheritance

If this principle is violated then there is a coupling between all clients

Polyad vs Monad

Monad is when properties are grouped into 1 single object that is then passed in a function parameter. Unfortunately this brings a dependency across all properties in that single object. Therefore its better to pass smaller objects (Polyad), in this way the dependencies are broken into smaller groups.

Interface Pollution

As we build up classes there is a tendency for us to add functionality that is specific for a particular implementation. In this way the interface gets populated by functions and properties that are not required if the class was in a different context, thus making the interface fat. In this way this violates the Liskov Substitution principle. Separate Clients means separate interfaces

There is a backward force applied by clients upon interfaces. For example a user may wish to add a trivial extra function that cannot be exactly positioned in existing interfaces.

Development Principles

Overall

Loose coupling

A fundamental goal of using a stack is loose coupling and tight cohesion. The various layers of the framework shouldn’t “know” about each other unless absolutely necessary.

For example, the template system knows nothing about Web requests, the database layer knows nothing about data display and the view system doesn’t care which template system a programmer uses.

Less code

Your apps should use as little code as possible; they should lack boilerplate. They should take full advantage of development languages dynamic capabilities, such as introspection.

Quick development

The point of a development framework in the 21st century is to make the tedious aspects of development fast. Your framework should allow for incredibly quick Web development.

Don’t repeat yourself (DRY)

Every distinct concept and/or piece of data should live in one, and only one, place. Redundancy is bad. Normalization is good.

The framework, within reason, should deduce as much as possible from as little as possible.

Explicit is better than implicit

This, a core principle, means that your app shouldn’t do too much “magic.” Magic shouldn’t happen unless there’s a really good reason for it. Magic is worth using only if it creates a huge convenience unattainable in other ways, and it isn’t implemented in a way that confuses developers who are trying to learn how to use the feature.

Consistency

Your framework should be consistent at all levels. Consistency applies to everything from low-level to high-level.

Models

Explicit is better than implicit

Fields shouldn’t assume certain behaviors based solely on the name of the field. This requires too much knowledge of the system and is prone to errors. Instead, behaviors should be based on keyword arguments and, in some cases, on the type of the field.

Include all relevant domain logic

Models should encapsulate every aspect of an “object,” following Martin Fowler’s Active Record design pattern.

This is why model-specific admin options are included in the model itself; data related to a model should be stored in the model.

Database API

The core goals of the database API are:

  • SQL efficiency

It should execute SQL statements as few times as possible, and it should optimize statements internally.

This is why developers need to call save() explicitly, rather than the framework saving things behind the scenes silently.

This is also why the select_related() QuerySet? method exists. It’s an optional performance booster for the common case of selecting “every related object.”

Terse, powerful syntax

The database API should allow rich, expressive statements in as little syntax as possible. It should not rely on importing other modules or helper objects.

Joins should be performed automatically, behind the scenes, when necessary.

Every object should be able to access every related object, systemwide. This access should work both ways.

Option to drop into raw SQL easily, when needed The database API should realize it’s a shortcut but not necessarily an end-all-be-all. The framework should make it easy to write custom SQL — entire statements, or just custom WHERE clauses as custom parameters to API calls.

URL design

Loose coupling

URLs in an app should not be coupled to the underlying code. Tying URLs to function names is a Bad And Ugly Thing.

Infinite flexibility

URLs should be as flexible as possible. Any conceivable URL design should be allowed.

Encourage best practices

The framework should make it just as easy (or even easier) for a developer to design pretty URLs than ugly ones.

File extensions in Web-page URLs should be avoided.

Vignette-style commas in URLs deserve severe punishment.

Definitive URLs

Technically, foo.com/bar and foo.com/bar/ are two different URLs, and search-engine robots (and some Web traffic-analyzing tools) would treat them as separate pages. A web app should make an effort to “normalize” URLs so that search-engine robots don’t get confused.

This is the reasoning behind the APPEND_SLASH setting.

Template system

Separate logic from presentation

We see a template system as a tool that controls presentation and presentation-related logic — and that’s it. The template system shouldn’t support functionality that goes beyond this basic goal.

If we wanted to put everything in templates, we’d be using PHP. Been there, done that, wised up.

Discourage redundancy

Be decoupled from HTML

The template system shouldn’t be designed so that it only outputs HTML. It should be equally good at generating other text-based formats, or just plain text.

XML should not be used for template languages

Using an XML engine to parse templates introduces a whole new world of human error in editing templates — and incurs an unacceptable level of overhead in template processing.

Assume designer competence

Treat whitespace obviously

The template system shouldn’t do magic things with whitespace. If a template includes whitespace, the system should treat the whitespace as it treats text — just display it. Any whitespace that’s not in a template tag should be displayed.

Don’t invent a programming language

The template system intentionally doesn’t allow the following:

  • Assignment to variables
  • Advanced logic

The goal is not to invent a programming language. The goal is to offer just enough programming-esque functionality, such as branching and looping, that is essential for making presentation-related decisions.

Safety and security

The template system, out of the box, should forbid the inclusion of malicious code — such as commands that delete database records.

This is another reason the template system doesn’t allow arbitrary Python code.

Extensibility

The template system should recognize that advanced template authors may want to extend its technology.

This is the philosophy behind custom template tags and filters.

Views

Simplicity

Writing a view should be as simple as writing a Python function. Developers shouldn’t have to instantiate a class when a function will do.

Use request objects

Views should have access to a request object — an object that stores metadata about the current request. The object should be passed directly to a view function, rather than the view function having to access the request data from a global variable. This makes it light, clean and easy to test views by passing in “fake” request objects.

Loose coupling

A view shouldn’t care about which template system the developer uses — or even whether a template system is used at all.

Differentiate between GET and POST

GET and POST are distinct; developers should explicitly use one or the other. The framework should make it easy to distinguish between GET and POST data.