Tribune DataViz

Matters of interest, from the data reporters and developers across Tribune Publishing

Best practices in web development with Python and Django

with 31 comments

At some point during the course of developing the Elections Center it became clear to me that each of us was spending too much development time trying to understand one another’s code. Sometimes this was because our intuitions about the right way to do things were different, other times it was a difference of priorities, but most often it was simply due to a lack of any explicit best practice.

Being something of a documentation and organization junky I took it upon myself to begin to collate a best practices guide. The hope was that in doing this we could both ensure craft knowledge was well-distributed as well as minimize the amount of “why did he do it that way?” moments when reading one another’s code. (More famously, WTFs/minute.) In our fast-paced environment there is little justification for being confused when it could have been avoided by simply writing it down.

What follows are the best practices we have established (by commission and arbitration) thus far. Some of them are tried-and-true policies (PEP 8), others are process-driven (the use of python’s logging module), and a few are entirely idiosyncratic (formatting of CSS files). All of them combine to provide us with explicit expectations for reading one another’s code and therefore a much easier time when are obligated to fix it at 10:30, on a Sunday, for a Monday morning paper.

We hope you will find them inspirational and make many suggestions about how they can be improved.

1. Version control

  • Excluding bug-fixes, active development should not be performed on the stable branch.
  • Large, discrete units of work should be developed on their own feature branches, so that another developer can pick it up if you get hit by a truck.
  • Feature branches should be prefixed with the owner’s initials to prevent confusion, e.g. “bb_polls”.
  • Feature branches should merge from master as often as is feasible, to prevent merge headaches later on.
  • Completed feature branches should be merged into master and deleted from the remote repository so that they are not mistaken for work-in-progress.
  • At the end of each iteration (when features are about to be deployed to production), master should be merged into stable and then stable HEAD should be tagged, e.g. “v3.0”. For example:
    git tag -a v3.0 -m "Deploying iteration 3."
    git push --tags
  • In the event a release needs to be redeployed with fixes, like when programming for new Zealand ipad pokies portals and websites, increment the sub-version index, e.g. “v3.1”
  • Follow the principle of “merge early, merge often” to prevent conflicts.
  • Commit messages should accurately summarize each task included in the commit, otherwise git log is useless.
  • Commit messages that reference tickets should use the #1234 syntax so that they are automatically associated in issue tracker.
  • Commit messages that close tickets should use the Closes #1234 syntax to automatically tie the commit to the appropriate ticket.
  • Always install dependencies in a mkvirtualenv --no-site-packages $name environment. This eliminates the possibility of version conflicts and makes setup as easy as pip install -r requirements.txt.

2. Python

  • Follow PEP 8 best practices, this means (among other things):
  • Four-space tabs are required.
  • Don’t use from package import *.
  • Do use whitespace liberally.
  • Do use descriptive lower_case_with_underscores variable names (understanding is much more important than brevity).
  • Do put explanatory docstrings on all non-trivial classes and methods.
  • If it isn’t covered by PEP 8, refer to Idiomatic Python, which is far more thorough.
  • Don’t use print. Use Python’s built-in logging so that output can be structured for different use-cases. (Also it makes transition to Py3k much easier.)
  • Always request a logger with log = logging.getLogger(""). Take note of the logger naming structure so that output can be filtered.
  • Organize imports in a logical manner: standard libraries first, then third-party libraries, then our own modules. Alphabetization within groups takes only a few seconds and makes removing cruft easier.
  • When in doubt, remember import this. (PEP 20)

3. Django

  • Follow the Django Coding style with regard to naming, spacing, etc.
  • Use pickled model fields with extreme caution.
  • Build normalized models and denormalize only when necessary for performance. When feasible, do so without altering the original table.
  • If you define a generic magic constant in a module (path, API key, url, etc) that is likely to be reused in another module define it in instead.
  • Use management commands instead of shell scripts. Remember that management commands can be invoked programmatically with call_command().
  • Don’t forget to activate the virtualenv when invoking a management command from cron:
    */5 * * * * source /home/newsapps/sites/$project/env/bin/activate && /home/newsapps/sites/$project/repository/manage $command >> /dev/null 2>&1
  • Always include the RequestContext when rendering view templates. For example:
    render_to_response(‘template’, template_dict, context_instance=RequestContext(request))
  • Put logic in view functions, not in templates. The simpler the template the better.

4. Javascript

  • Don’t use jQuery in widgets that will end up on other sites.
  • Otherwise, use jQuery.
  • Whenever possible, put Javascript at the bottom of the page.
  • Don’t use inline Javascript.


  • Add a top level ID to every kind of page (#race-page, #candidate-page)
  • Use IDs for elements that are unique to every page in the site. Use classes for everything else.
  • Use hyphens for IDs and classes, no camelCase or underscores.
  • Use descriptive and unique names for IDs. Use simple generic names for classes.
  • Always define classes with nested rules: #candidate-page .box-column .box .bar-chart. Use your best judgment, don’t go overboard with the nested rules, just enough to make sure your not going to style the wrong things. IDs rarely need to be nested
  • Each CSS selector gets its own line. The block of rules has to share. It makes big CSS scannable:
    a:link {color:#336699;}
    a:active {color:#6d9fd2;}
    a:visited {color:#336699;}
    a:visited:hover {color:#6d9fd2;}
  • Don’t rely on (because of IE6):
    • min-width, max-width, min-height, max-height
    • overflow:hidden
    • css tables
    • attribute selectors
    • psudo-classes (other than for a tags)
    • > (child selector)
    • position:fixed
  • If you are positioning one block absolutely inside another, make sure the parent has a specified size.
  • Don’t use inline styles.

6. Images

  • Don’t use them.
  • If you use them, make sure you use photoshop’s “Save for web…” or figure out another way to optimize them.
  • Use jpeg for images with lots of different colors and gradients, png/gif for images with few colors.
  • PNG rules (if you’re relying on PNG fix)

7. Testing

  • Use unit tests to validate non-trivial methods.
  • Always test methods that involve date math, statistical calculations, non-ducky type conversions, or extract data from arbitrary sources (Excel, web, etc).

Written by Christopher Groskopf

February 26, 2010 at 1:27 pm

Posted in Craft, Python

31 Responses

Subscribe to comments with RSS.

  1. I really like the way you’ve done this. Very concise and clear.

    David Chandek-Stark

    February 26, 2010 at 8:22 pm

  2. […] of checklist that Gawande writes and speaks about. Here’s something closer to what he means: Best practices in web development with Python and Django. That list comes from Christopher Groskopf, a web developer at the Chicago Tribune, who writes: […]

  3. Recommendation for #4: Javascript
    Avoid global variables. Use module pattern:

    Yuri Victor

    March 4, 2010 at 11:04 am

  4. […] #2: Christopher Groskopf’s Python/Django development checklist. […]

  5. […] Best Practices in Web Development with Django and Python — great set of recommendations. (via Jon Udell’s article on checklists) […]

  6. How about prefixing feature branch names with the ID of issue in whatever issue tracker you are using?


    March 5, 2010 at 7:38 am

  7. I’d advise against deleting complete branches. Upon merge-back-into-trunk, rename them to a clearly defined prefix (e.g. “Completed_bb_polls”).


    March 5, 2010 at 10:21 am

  8. This is awesome. Five of these rules are great for any work in any framework, not just Django.

    I have to disagree with #5 “Always define classes with nested rules.” Down that path lies madness.

    This overloads selectors and leads to a proliferation of namespaces and identical classes, or worse: _nearly_ identical classes. This in turn causes the design to drift, ugh. I’m fighting this battle right now refactoring old display code.

    It’s tempting to make the CSS do both semantic AND cosmetic duty but fight that temptation. If you want a different cosmetic effect in a certain semantic context, seek out that context and alter the HTML _in the template_. Don’t make the stylesheet grep for you.

    Paul Souders

    March 5, 2010 at 6:28 pm

  9. […] Best practices in web development with Python and Django « News Apps Blog Nicely done. (tags: development django python bestpractices webdev) […]

  10. […] Shared Best practices in web development with Python and Django « News Apps Blog. […]

  11. HTML/CSS:

    Proper semantics, markup minimalism, complete separation of content, presentation and behavior are not your enemy. They make FED work easier/faster.

    It’s okay to use tables for tables. Rows? Columns? That’s a table. For anything else, you get the dipping chair.

    Avoid padding on block elements with defined dimensions – to avoid IE 6’s busted box model. Correct box model: left/right padding+width = true width. IE 6 Box Model: width = true width. left/right padding smooshes content inward. Not an issue until the relevant dimension is defined.

    Don’t !important – it never ends up being temporary. Just don’t.

    Make sure everybody actually understands that z-index is only relevant to siblings with some kind of positioning defined to avoid the arms race that ends when somebody discovers that 999999999999999999 fails even if you’re actually hitting the right parent element with it to achieve the desired effect.

    Padding includes background image space. Nice for bullets when it can’t be a list item.

    Sort out a form element scheme for your CSS reset where you can just plop everything down and have line up properly and neatly.

    Don’t use HTML comments between floated content elements. (peekaboo bug)

    Fight designers on selects and other form elements in dropdowns in modals within draggable modals. I mean hit them. Hard. Your sanity could be at stake.

    If you do actually have to layer form elements over and under things, familiarize yourself with the iframe technique for keeping them from popping through other elements. JQ has a plugin.


    Write OOP-oriented JS for stuff where it makes sense to. Portability in huge web app == good!

    JQuery selector speed: IDs or narrowing down with IDs is ideal, tags.classes are okay when necessary. .classes are really freaking slow on a large HTML doc in IE.

    Don’t track conditions with a variable when you can simply check the environment itself whenever you need to. No changes needed when the environment gets altered or the scope of the function/object changes, or maybe somebody’s math stinks.

    If you can handle most or a part of a problem with CSS rather than JS, do so. It’s much more efficient.

    Don’t inject HTML via DOM methods. Inner HTML is much faster. (I believe JQ uses it whenever possible).

    Keep those JQ Dom Ready function calls in one place. If one throws an exception the rest of them fail. (one of the few things I find yucky about JQ)

    Throw your own exceptions. It’s easier than you think.

    Jealous ECOM FED

    March 8, 2010 at 10:15 pm

  12. The general user experience is by far the most under-used and under-appreciated aspect of web development.

  13. Not using PNGs? WHAT?

    pngs are made for the web, if you won’t use them because you care about the shit of ie6, then tell that idiots to update their crappy shit of browser!


    March 24, 2010 at 1:04 pm

  14. This is very helpful! Thanks for writing it down.


    March 24, 2010 at 1:13 pm

  15. Agre with everything apart from the very bold statement about PNG you used: “Don’t use PNGs”. PNGs are quite common in a lot of modern websites and ultimately a format made for web use.

    This yet again, comes down to the whole god awful browser that is IE6.


    March 24, 2010 at 1:42 pm

  16. Great list, even for non-python & django developers.


    March 24, 2010 at 1:49 pm

  17. Brilliant work there. Thanks a lot for penning down things in a crisp and clear manner :)

    Sriram Murali

    March 24, 2010 at 9:12 pm

  18. […] Best practices in web development with Python and Django 0 […]

  19. Please, change the “font-family” (“calluna-1″,”calluna-2”,serif) of your blog. It´s difficult to read.


    March 25, 2010 at 11:55 am

  20. […] Best practices in web development with Python and Django « News Apps Blog […]

  21. You’ve gathered a lot of small tips from many areas of web development. Not too much Django here, so let me point you to

    You mentioned virtual-env in cron, but doesn’t explain its usage in workflow. You’ve forgot to add debugging with debug_toolbar and logging. Model migrations are absent.

    Why do you recommend to always use RequestContext? It’s obvious, that some cases doesn’t require that and including RequestContext adds unnecessary overhead to the view function.


    April 26, 2010 at 11:09 am

    • Sergray, thanks for the feedback. I’ll do my best to address your concerns regarding the post.

      You’ve gathered a lot of small tips from many areas of web development. Not too much Django here

      That’s not by accident. Django itself does an awfully good job of enforcing best practices and, much like Python itself, the obvious solution is typically the correct one. In this post we’ve only made the effort to document things which we felt would benefit from being made more explicit.

      Nice post, its always good to have a window into how others work.

      You mentioned virtual-env in cron, but doesn’t explain its usage in workflow.

      I’m not sure I follow. If you’re asking about virtualenv with regards to our development workflow, I’ll point you to what I wrote above. The best way to use virtualenv is pretty much the only way to use virtualenv–we didn’t feel like it would benefit from further documentation.

      You’ve forgot to add debugging with debug_toolbar and logging.

      Not forgotten–we just don’t have any canonical rules for handling them. Debug toolbar can be very useful and logging is essential, but in general we don’t have any hard and fast rules for working with them beyond what is mentioned in the Python section.

      Model migrations are absent.

      That is because we don’t do them! In all seriousness we’ve yet to have a reason to have to deal with migrations. I hope once we do we will have some additional best practices to document regarding that process.

      Why do you recommend to always use RequestContext? It’s obvious, that some cases doesn’t require that and including RequestContext adds unnecessary overhead to the view function.

      We recommend to always include it because otherwise we find ourselves expecting to have access to it and then when its not available we inevitably spend ten minutes debugging a non-issue. Always including it saves us that wasted development time. With regards to performance I say simply: optimize last. I’m sure there is some performance overhead, but its probably trivial, and if it isn’t then we will deal with it on the day it becomes a problem. So far it hasn’t caused any issues for us.

      Thanks again for all the great feedback Sergray! I hope I’ve addressed some of the questions you’ve raised regarding the post.

      Christopher Groskopf

      April 26, 2010 at 11:32 am

  22. Regarding #6… Coming from a design background but understanding the web development world, I understand how annoying it can be to have images that are not optimized for the web.

    Tampa Web Design

    January 17, 2011 at 8:52 pm

  23. Interesting views. I wonder how much of this is still relevant with HTML5 changing the playing field with regard to web development.

    Tampa Bay Photography

    December 30, 2011 at 6:47 pm

  24. Best practices in web development with Python and Django News Apps Blog is a great post. I am going to take more time researching this subject.

  25. Thank you for your input. Very interesting article. I will refer my web people here.

    Tampa Kitchen Cabinets

    March 16, 2012 at 4:25 pm

  26. @sergray – thank you for the link, there was lots of helpful info on it.

  27. […] Best Practices in Web Development with Django and Python — great set of recommendations. (via Jon Udell‘s article on checklists) […]

  28. […] Best practices in web development with Python and Django « News Apps Blog. […]

  29. […] URL: Buenas practicas […]

  30. […] on the web, reading about django dev best practices points to use pickled model fields with extreme […]

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

%d bloggers like this: