<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>muhuk.com</title>
	<atom:link href="http://www.muhuk.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.muhuk.com</link>
	<description>know thyself</description>
	<lastBuildDate>Thu, 02 Sep 2010 07:13:59 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>I Am Discontinuing Telvee</title>
		<link>http://www.muhuk.com/2010/09/i-am-discontinuing-telvee/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=i-am-discontinuing-telvee</link>
		<comments>http://www.muhuk.com/2010/09/i-am-discontinuing-telvee/#comments</comments>
		<pubDate>Thu, 02 Sep 2010 07:13:59 +0000</pubDate>
		<dc:creator>Atamert Ölçgen</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[Personal]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[django-inviting]]></category>
		<category><![CDATA[django-simple-friends]]></category>
		<category><![CDATA[telvee]]></category>

		<guid isPermaLink="false">http://www.muhuk.com/?p=380</guid>
		<description><![CDATA[Telvee was originally a Facebook app. It was Burak Büyükdemir&#8216;s idea to create a virtual coffee reading app. Rakı Sofrası was super popular then. We have quickly built and deployed and getting some good results. But the competition wasn&#8217;t fair. When I decided to give this software a fair chance to succeed on its very [...]]]></description>
			<content:encoded><![CDATA[<p>Telvee was originally a Facebook app. It was <a href="http://burakbuyukdemir.com/">Burak Büyükdemir</a>&#8216;s idea to create a virtual coffee reading app. <a href="http://apps.facebook.com/rakisofrasi/">Rakı Sofrası</a> was super popular then. We have quickly built and deployed and getting some good results. But the competition wasn&#8217;t fair.</p>

<p>When I decided to give this software a fair chance to succeed on its very own domain the only question in my head was; will it pass the test of users? It doesn&#8217;t really matter what you have intended the users do with your application. What matters, first, is what they think they&#8217;d like to do with it and then whether or not they actually use it.</p>

<p>So I tried and I failed. Two main reasons of this failure are; technical deficiencies and the special way of interaction coffee reading is. Technical deficiencies is the easy one. I couldn&#8217;t devote enough time for telvee, especially lately. As a result it doesn&#8217;t even have basic stuff like e-mail changing or account deletion. This is 100% my fault. The second reason however is more complicated and there was not much I could do about it. Except one thing I will tell you at the end of this post.</p>

<p>Telvee didn&#8217;t pass the user&#8217;s test mainly because coffee reading is somewhat private. It&#8217;s more of a 1-to-1 communication, while all social applications<sup>1</sup> are designed for 1-to-many communication. This was disastrous not only because of the lack of viral growth but also because of the reluctance of users to interact with other users.</p>

<div id="attachment_383" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.muhuk.com/wp-content/uploads/2010/08/muhuk-92.jpeg"><img src="http://www.muhuk.com/wp-content/uploads/2010/08/muhuk-92-300x69.jpg" alt="An example image of coffee remains telvee generates" title="An example image of coffee remains telvee generates" width="300" height="69" class="size-medium wp-image-383" /></a><p class="wp-caption-text">An example image of coffee remains telvee generates</p></div>

<p>I would like to thank everybody who participated and I hope you had some fun playing with it. Telvee domain will soon redirect to this post and I will probably not renew it next time.</p>

<p>I won&#8217;t be starting a new experiment soon. I will spend most of my time on my work. Hopefully I will be spending a little more time on free software projects. By the way I would like to note that Telvee has spawned a couple of Django apps: <a href="http://github.com/muhuk/django-inviting">django-inviting</a> &amp; <a href="http://github.com/muhuk/django-simple-friends">django-simple-friends</a>.</p>

<p>Oh, and the thing I could do better about user experience was to be more agile. I should have either fixed the problem quickly or failed fast. I have no regrets though. This was a unique experience and I have learned a lot.</p>

<hr />

<p><strong>1</strong>: Yes, even the e-mail system and dating sites are designed for 1-to-many communication primarily in mind.</p>


<p>Related posts:<ol><li><a href='http://www.muhuk.com/2009/10/new-project-telvee/' rel='bookmark' title='Permanent Link: New Project: Telvee'>New Project: Telvee</a></li>
<li><a href='http://www.muhuk.com/2009/06/psychic-irc-support-in-10-easy-steps/' rel='bookmark' title='Permanent Link: Psychic IRC Support In 10 Easy Steps'>Psychic IRC Support In 10 Easy Steps</a></li>
<li><a href='http://www.muhuk.com/2010/01/dynamic-translation-apps-for-django/' rel='bookmark' title='Permanent Link: Dynamic Translation Apps for Django'>Dynamic Translation Apps for Django</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.muhuk.com/2010/09/i-am-discontinuing-telvee/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How To Create A Debian VM With Qemu</title>
		<link>http://www.muhuk.com/2010/07/how-to-create-a-debian-vm-with-qemu/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=how-to-create-a-debian-vm-with-qemu</link>
		<comments>http://www.muhuk.com/2010/07/how-to-create-a-debian-vm-with-qemu/#comments</comments>
		<pubDate>Wed, 07 Jul 2010 10:26:39 +0000</pubDate>
		<dc:creator>Atamert Ölçgen</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[qemu]]></category>
		<category><![CDATA[share]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[vm]]></category>

		<guid isPermaLink="false">http://www.muhuk.com/?p=375</guid>
		<description><![CDATA[I would like to post my notes as a little tutorial here. I am usually using these virtual machines as cheap staging servers. The first part of this tutorial, you hopefully need to do only once: creating a fresh Debian system. In the second part we will build on this image to create many different [...]]]></description>
			<content:encoded><![CDATA[<p>I would like to post my notes as a little tutorial here. I am usually using these virtual machines as cheap staging servers. The first part of this tutorial, you hopefully need to do only once: creating a fresh Debian system. In the second part we will build on this image to create many different servers.</p>

<h2>Creating A Base Debian System</h2>

<p>We will create a Qemu machine and install Debian Lenny on it first:</p>

<pre><code># Download Debian image
wget http://debian.osuosl.org/debian-cdimage/current/i386/iso-cd/debian-504-i386-businesscard.iso

# Create base VM image
qemu-img create -f qcow2 debian.qcow2 2G
</code></pre>

<p>Our disk image will have a 2 Gigabyte size limit. You can pick a different size if you need.</p>

<p>Now we need to power on our VM and install Debian:</p>

<pre><code># Install Debian
qemu -enable-kvm -k tr -cdrom debian-504-i386-businesscard.iso -hda debian.qcow2 -boot d
</code></pre>

<p>You don&#8217;t need to allocate a large swap disk, 128MB should do just fine for a file/web server. Also I wouldn&#8217;t bother creating a seperate partition for <code>/home/</code>.</p>

<p>Next let&#8217;s log in as the user (<code>www</code> here) we have created to make final changes:</p>

<pre><code># logged in as user
dpkg-reconfigure console-data
aptitude install ssh sudo
echo "www ALL=(ALL) ALL" &gt;&gt; /etc/sudoers
</code></pre>

<p>At this point you might want to take a backup of <code>debian.qcow2</code>. (Even though we will open it only read-only from now on)</p>

<h2>Creating The Actual VM</h2>

<p>To save time and space we will use copy-on-write disks and re-use <code>debian.qcow2</code>.</p>

<pre><code># Create the actual VM's disk
qemu-img create -f qcow2 -o backing_file=debian.qcow2 actual.qcow2
</code></pre>

<p>Actually we are done. You can log in to your VM using the following command and start installing/configuring/running:</p>

<pre><code>qemu -enable-kvm -k tr -hda actual.qcow2 -net user -net nic \
                                         -redir tcp:5022::22 \
                                         -redir tcp:9080::80
</code></pre>

<p>A few things to note about the command above:</p>

<ul>
<li><code>-enable-kvm</code> is meaningful only if you have <code>kvm</code> kernel module installed. It improves performance a great deal, so it&#8217;s highly recommended.</li>
<li>You probably need to change <code>-k tr</code> according to your keyboard&#8217;s layout.</li>
<li>We are setting up two TCP redirections. 22 is for SSH and 80 is for HTTP. You can add more ports if you need.</li>
</ul>

<p>Finally, I suggest you to prefer SSHing your VM instead of logging in directly:</p>

<pre><code># SSH into the VM
ssh -p 5022 www@localhost
</code></pre>

<p>I hope some of you find this useful.</p>


<p>Related posts:<ol><li><a href='http://www.muhuk.com/2009/04/galfthw-style-tutorial-on-python-coroutines/' rel='bookmark' title='Permanent Link: GALFTHW Style Tutorial On Python Coroutines'>GALFTHW Style Tutorial On Python Coroutines</a></li>
<li><a href='http://www.muhuk.com/2009/05/freedays09-videos/' rel='bookmark' title='Permanent Link: Freedays&#8217;09 Videos'>Freedays&#8217;09 Videos</a></li>
<li><a href='http://www.muhuk.com/2009/06/psychic-irc-support-in-10-easy-steps/' rel='bookmark' title='Permanent Link: Psychic IRC Support In 10 Easy Steps'>Psychic IRC Support In 10 Easy Steps</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.muhuk.com/2010/07/how-to-create-a-debian-vm-with-qemu/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>How to Install MySQL with Fabric</title>
		<link>http://www.muhuk.com/2010/05/how-to-install-mysql-with-fabric/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=how-to-install-mysql-with-fabric</link>
		<comments>http://www.muhuk.com/2010/05/how-to-install-mysql-with-fabric/#comments</comments>
		<pubDate>Sat, 22 May 2010 06:51:12 +0000</pubDate>
		<dc:creator>Atamert Ölçgen</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[deployment]]></category>
		<category><![CDATA[fabric]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://www.muhuk.com/?p=372</guid>
		<description><![CDATA[I just want to share a small fabfile snipplet that installs mysql-server package on a Debian machine if it&#8217;s not already installed. Actually it should have been quite straightforward; just issue an apt-get command, right? But during configuration it displays a curses dialog for MySQL root password. This of course blows your automated configuration plans. [...]]]></description>
			<content:encoded><![CDATA[<p>I just want to share a small <a href="http://fabfile.org/">fabfile</a> snipplet that installs <code>mysql-server</code> package on a Debian machine if it&#8217;s not already installed. Actually it should have been quite straightforward; just issue an <code>apt-get</code> command, right? But during configuration it displays a curses dialog for MySQL root password. This of course blows your automated configuration plans. We will seed this value to <code>debconf</code> database to avoid this dialog.</p>

<p>First let&#8217;s have a look at the supporting code:</p>

<p><code><pre style="color:#000000; background-color:#ffffff; font-size:10pt; font-family:'Courier New';"><span style="color:#000000; font-weight:bold">from</span> &#95;&#95;future&#95;&#95; <span style="color:#000000; font-weight:bold">import</span> with&#95;statement
<span style="color:#000000; font-weight:bold">from</span> fabric<span style="color:#000000">.</span>api <span style="color:#000000; font-weight:bold">import</span> <span style="color:#000000">*</span>
<span style="color:#000000; font-weight:bold">from</span> fabric<span style="color:#000000">.</span>utils <span style="color:#000000; font-weight:bold">import</span> warn</p>

<p><span style="color:#000000; font-weight:bold">def</span> <span style="color:#010181">apt&#95;get</span><span style="color:#000000">(*</span>packages<span style="color:#000000">):</span>
    <span style="color:#010181">sudo</span><span style="color:#000000">(</span><span style="color:#ff0000">'apt-get -y --no-upgrade install %s'</span> <span style="color:#000000">%</span> <span style="color:#ff0000">' '</span><span style="color:#000000">.</span><span style="color:#010181">join</span><span style="color:#000000">(</span>packages<span style="color:#000000">),</span> shell<span style="color:#000000">=</span><span style="color:#000000; font-weight:bold">False</span><span style="color:#000000">)</span>
</pre></code></p>

<p>Our <code>apt_get</code> fabric command issues <code>apt_get install</code> that answers <code>yes</code> to all questions and does not upgrade if the requested package is already installed. Now let&#8217;s take a look at the main command that installs <code>mysql-server</code>:</p>

<p><code><pre style="color:#000000; background-color:#ffffff; font-size:10pt; font-family:'Courier New';"><span style="color:#000000; font-weight:bold">def</span> <span style="color:#010181">install&#95;mysql</span><span style="color:#000000">():</span>
    with <span style="color:#010181">settings</span><span style="color:#000000">(</span><span style="color:#010181">hide</span><span style="color:#000000">(</span><span style="color:#ff0000">'warnings'</span><span style="color:#000000">,</span> <span style="color:#ff0000">'stderr'</span><span style="color:#000000">),</span> warn&#95;only<span style="color:#000000">=</span><span style="color:#000000; font-weight:bold">True</span><span style="color:#000000">):</span>
        result <span style="color:#000000">=</span> <span style="color:#010181">sudo</span><span style="color:#000000">(</span><span style="color:#ff0000">'dpkg-query --show mysql-server'</span><span style="color:#000000">)</span>
    <span style="color:#000000; font-weight:bold">if</span> result<span style="color:#000000">.</span>failed <span style="color:#000000; font-weight:bold">is False</span><span style="color:#000000">:</span>
        <span style="color:#010181">warn</span><span style="color:#000000">(</span><span style="color:#ff0000">'MySQL is already installed'</span><span style="color:#000000">)</span>
        <span style="color:#000000; font-weight:bold">return</span>
    mysql&#95;password <span style="color:#000000">=</span> <span style="color:#010181">prompt</span><span style="color:#000000">(</span><span style="color:#ff0000">'Please enter MySQL root password:'</span><span style="color:#000000">)</span>
    <span style="color:#010181">sudo</span><span style="color:#000000">(</span><span style="color:#ff0000">'echo &quot;mysql-server-5.0 mysql-server/root&#95;password password '</span> \
                              <span style="color:#ff0000">'%s&quot; | debconf-set-selections'</span> <span style="color:#000000">%</span> mysql&#95;password<span style="color:#000000">)</span>
    <span style="color:#010181">sudo</span><span style="color:#000000">(</span><span style="color:#ff0000">'echo &quot;mysql-server-5.0 mysql-server/root&#95;password&#95;again password '</span> \
                              <span style="color:#ff0000">'%s&quot; | debconf-set-selections'</span> <span style="color:#000000">%</span> mysql&#95;password<span style="color:#000000">)</span>
    <span style="color:#010181">apt&#95;get</span><span style="color:#000000">(</span><span style="color:#ff0000">'mysql-server'</span><span style="color:#000000">)</span>
</pre></code></p>

<p>First we want to make sure <code>mysql-server</code> is not already installed. For this reason we issue <code>dpkg-query --show mysql-server</code>. Note that if this command fails then <code>mysql-server</code> is not installed and we can proceed otherwise we want to return. We hide the ugly error messages by running our <code>dpkg-query</code> command within a <code>settings</code> context:</p>

<p><code><pre style="color:#000000; background-color:#ffffff; font-size:10pt; font-family:'Courier New';">with <span style="color:#010181">settings</span><span style="color:#000000">(</span><span style="color:#010181">hide</span><span style="color:#000000">(</span><span style="color:#ff0000">'warnings'</span><span style="color:#000000">,</span> <span style="color:#ff0000">'stderr'</span><span style="color:#000000">),</span> warn&#95;only<span style="color:#000000">=</span><span style="color:#000000; font-weight:bold">True</span><span style="color:#000000">):</span>
    result <span style="color:#000000">=</span> <span style="color:#010181">sudo</span><span style="color:#000000">(</span><span style="color:#ff0000">'dpkg-query --show mysql-server'</span><span style="color:#000000">)</span>
<span style="color:#000000; font-weight:bold">if</span> result<span style="color:#000000">.</span>failed <span style="color:#000000; font-weight:bold">is False</span><span style="color:#000000">:</span>
    <span style="color:#010181">warn</span><span style="color:#000000">(</span><span style="color:#ff0000">'MySQL is already installed'</span><span style="color:#000000">)</span>
    <span style="color:#000000; font-weight:bold">return</span>
</pre></code></p>

<p>The rest of the code is pretty straightforward. We prompt to the user running fabric for the root password and seed its value twice into the <code>debconf</code> database.</p>

<p><code><pre style="color:#000000; background-color:#ffffff; font-size:10pt; font-family:'Courier New';"><span style="color:#010181">sudo</span><span style="color:#000000">(</span><span style="color:#ff0000">'echo &quot;mysql-server-5.0 mysql-server/root&#95;password password '</span> \
                            <span style="color:#ff0000">'%s&quot; | debconf-set-selections'</span> <span style="color:#000000">%</span> mysql&#95;password<span style="color:#000000">)</span>
<span style="color:#010181">sudo</span><span style="color:#000000">(</span><span style="color:#ff0000">'echo &quot;mysql-server-5.0 mysql-server/root&#95;password&#95;again password '</span> \
                            <span style="color:#ff0000">'%s&quot; | debconf-set-selections'</span> <span style="color:#000000">%</span> mysql&#95;password<span style="color:#000000">)</span>
<span style="color:#010181">apt&#95;get</span><span style="color:#000000">(</span><span style="color:#ff0000">'mysql-server'</span><span style="color:#000000">)</span>
</pre></code></p>

<p>Finally, having finished our little dance, we install <code>mysql-server</code>. I hope this helps some automated deployment believer out there.</p>


<p>Related posts:<ol><li><a href='http://www.muhuk.com/2010/01/quest-for-ultimate-developmentdeployment-toolset-fabric-pip-virtualenv/' rel='bookmark' title='Permanent Link: Quest For Ultimate Development/Deployment Toolset: Fabric, Pip &#038; Virtualenv'>Quest For Ultimate Development/Deployment Toolset: Fabric, Pip &#038; Virtualenv</a></li>
<li><a href='http://www.muhuk.com/2009/07/django-renderformplain/' rel='bookmark' title='Permanent Link: django-renderformplain'>django-renderformplain</a></li>
<li><a href='http://www.muhuk.com/2010/01/dynamic-translation-apps-for-django/' rel='bookmark' title='Permanent Link: Dynamic Translation Apps for Django'>Dynamic Translation Apps for Django</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.muhuk.com/2010/05/how-to-install-mysql-with-fabric/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>My Idea Of The Django Blogging App™</title>
		<link>http://www.muhuk.com/2010/04/my-idea-of-the-django-blogging-app%e2%84%a2/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=my-idea-of-the-django-blogging-app%25e2%2584%25a2</link>
		<comments>http://www.muhuk.com/2010/04/my-idea-of-the-django-blogging-app%e2%84%a2/#comments</comments>
		<pubDate>Wed, 28 Apr 2010 09:36:48 +0000</pubDate>
		<dc:creator>Atamert Ölçgen</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[blogs]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[reusable]]></category>

		<guid isPermaLink="false">http://www.muhuk.com/?p=370</guid>
		<description><![CDATA[I am not going to talk about yet another Django-based blogging engine in this post. There are a number of blogging apps which try to be like turn-key solutions, like a WordPress blog. I have skimmed through the code of many such apps, but haven&#8217;t used one yet. Some of them are really high quality [...]]]></description>
			<content:encoded><![CDATA[<p>I am not going to talk about yet another Django-based blogging engine in this post. There are a number of blogging apps which try to be like turn-key solutions, like a WordPress blog. I have skimmed through the code of many such apps, but haven&#8217;t used one yet. Some of them are really high quality apps. What I have in mind is somewhat different though. I would like an app that would allow me to build a blog that satisfies my projects specific requirements.</p>

<p>Let me reiterate the last sentence. Having a Django-based blog just because Django is fashinable is a little dumb in my opinion. If Django-based X blogging engine suits you better than anything else, use it. Why not? But my personal choice of blogging engine is WordPress<sup>1</sup>. The value of a Django blogging app, for me, is in <em>adding</em> a blog to a Django project. And different projects might have different requirements. So my idea of a Django blogging app is one that is highly configurable and highly extendable.</p>

<p>On the other hand I don&#8217;t need the convenience of clicking a checkbox on a polished UI. I can write a function. Or I don&#8217;t necessarily need it to, say, provide a navigation menu. There are apps that do that. Even if there wasn&#8217;t it shouldn&#8217;t be the blog app&#8217;s job. So I am not looking for an instant-blog. I have a Django app in my mind, nothing more.</p>

<h3>What Should Be Left Out</h3>

<p>Basically any feature that can be provided by another <a href="http://www.muhuk.com/tag/reusable/">reusable app</a> should be left out. Why should we re-implement something that is already done&#8230; and reviewed by others&#8230; and tested. Of course this doesn&#8217;t necessarily mean providing no convenience functions.</p>

<ul>
<li>No admin. Because we already have <a href="http://docs.djangoproject.com/en/dev/ref/contrib/admin/">one</a>.</li>
<li>No theming. For the love of Flying Spaghetti Monster, you don&#8217;t need any <em>theming</em> other than what <code>django.template</code> offers. Pre-built themes are for turn-key solutions.</li>
<li>No comments or contact forms. (See <a href="http://docs.djangoproject.com/en/dev/ref/contrib/comments/"><code>django.contrib.comments</code></a> and <a href="http://bitbucket.org/ubernostrum/django-contact-form/"><code>django-contact-form</code></a>)</li>
<li>No <em>official</em> markup format (or formats). This can be handled in the templates without difficulty. But, maybe, pluggable content filters is a good idea. I haven&#8217;t made up my mind on this one entirely. It won&#8217;t use any markup format by default, that is for sure.</li>
</ul>

<h3>What Should Be Included</h3>

<p>Remember, every project has a different set of needed features for its blog. Some need catagories, some need tags and some others need both. But it would end up as a disaster if we implemented each one of those features into <a href="http://www.muhuk.com/2010/01/developing-reusable-django-apps/">a single app</a>. Instead I think it should consist of many small apps that work together. But I wouldn&#8217;t want to end up having huge spaghetti of apps that all depend on one another, like <a href="http://pinaxproject.com/">Pinax</a> does. A minimal amount of core apps<sup>2</sup> and then everything else should be optional. By optional I mean you don&#8217;t have to install packages you won&#8217;t need.</p>

<p>I think the components (apps) should be activated via adding to <code>INSTALLED_APPS</code> and configured with settings. I can&#8217;t think of any parameter that needs to be changed dynamically, so why not use the established way of doing configuration in Django.</p>

<p>Two must have features for such a blogging app are previews and scheduled publishing. It is possible that you sometimes write a post quickly and publish it immediately. But I suppose nobody will say they don&#8217;t care about these two features.</p>

<p>Built-in feeds and sitemaps are also nice to have.</p>

<p>Multiple instances of this blogging app running on the same project? À la <code>admin</code>. I can&#8217;t make my mind on this one. Sure it would be a nice feature. But it could complicate the code. Peehaps too much for a not so common case.</p>

<p>What do you think about the general idea? Are there any other <em>must-have</em> features? Would you be willing to learn a new app when you are already comfortable with another blogging app?</p>

<hr />

<p><strong>1:</strong> Even though it&#8217;s written in the abomination called PHP. But since there are plugins for everything I don&#8217;t have to touch the code.</p>

<p><strong>2:</strong> One sounds like a good number, if possible.</p>


<p>Related posts:<ol><li><a href='http://www.muhuk.com/2009/03/django-where-should-my-app-live/' rel='bookmark' title='Permanent Link: Django: Where Should My App Live'>Django: Where Should My App Live</a></li>
<li><a href='http://www.muhuk.com/2010/01/developing-reusable-django-apps-app-settings/' rel='bookmark' title='Permanent Link: Developing Reusable Django Apps: App Settings'>Developing Reusable Django Apps: App Settings</a></li>
<li><a href='http://www.muhuk.com/2010/01/developing-reusable-django-apps/' rel='bookmark' title='Permanent Link: Developing Reusable Django Apps'>Developing Reusable Django Apps</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.muhuk.com/2010/04/my-idea-of-the-django-blogging-app%e2%84%a2/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>Why Not To Localize Community Support</title>
		<link>http://www.muhuk.com/2010/04/why-not-to-localize-community-support/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=why-not-to-localize-community-support</link>
		<comments>http://www.muhuk.com/2010/04/why-not-to-localize-community-support/#comments</comments>
		<pubDate>Fri, 16 Apr 2010 18:28:51 +0000</pubDate>
		<dc:creator>Atamert Ölçgen</dc:creator>
				<category><![CDATA[Personal]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[community]]></category>
		<category><![CDATA[free software]]></category>
		<category><![CDATA[netiquette]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[share]]></category>

		<guid isPermaLink="false">http://www.muhuk.com/?p=367</guid>
		<description><![CDATA[If you are reading this, you know English. I would like to pose a question before we go any further; would you prefer community support in your native language over what international community offers?1 By community support, I mean free software support provided by the user community. Every successful project has some form of communication; [...]]]></description>
			<content:encoded><![CDATA[<p>If you are reading this, you know <a href="http://www.muhuk.com/2009/04/what-youth-needs-is-vision/">English</a>. I would like to pose a question before we go any further; would you prefer community support in your native language over what international community offers?<sup>1</sup></p>

<p>By <code>community support</code>, I mean <a href="http://www.fsf.org/">free software</a> support provided by the user community. Every successful project has some form of communication; mailing lists, forums, IRC, wikis, etc. And the preferred language is almost always English. Simply because you can reach more people. People who can use your code. Who can test it, file bugs, send patches, etc. It is the <strong>logical</strong> thing to do. But in the spirit of freedom, I think nobody should be forced to use a certain (natural or programming) language for something they have created.</p>

<h3>Real Communities</h3>

<p>I think having local communities is a great idea. I don&#8217;t know, because we don&#8217;t have any in Türkiye<sup>2</sup>. So I am just guessing they should be cultivating and fun at the same time. We do have many local <a href="http://en.wikipedia.org/wiki/Community_building">pseudo-communities</a> though. There is some activity, people come and go, some of them stay much longer than the others. But they never progress towards a community. I think this is because they make the fundamental mistake of localizing community support. They translate documentation intended for highly technical people and create new channels that no advanced user will bother to participate. In short, <strong>they attempt to sidestep the language barrier</strong>.</p>

<p>If want to be a <a href="http://www.catb.org/~esr/faqs/hacker-howto.html#skills4">programmer</a> you need to know English. It is not optional. It is not a requirement of being a good programmer. You are at most an excellent <a href="http://www.urbandictionary.com/define.php?term=script+kiddie">script kiddie</a> without proper English skills. Obviously, knowing perfect English doesn&#8217;t make you a great programmer instantly. It will increase the resources you can access dramatically, though. And most importantly it will give you the chance to <a href="http://jangosteve.com/post/380926251/no-one-knows-what-theyre-doing">know more about what you don&#8217;t know</a>. Stay in your little world to play with your mates <strong>who know just as little as you do</strong> and you will never improve.</p>

<h3>A Foolish Endeavour</h3>

<p>Some people, who spend time with these pseudo-communities, know English well enough. They are probably acting with good intentions when they provide support in their native language. But they are actually wasting their time. Having been solved one specific problem doesn&#8217;t make the other person a better programmer. On the contrary they are giving local users another reason to avoid solving the real issue. The function of a community should be supporting each member&#8217;s continuous improvement. That doesn&#8217;t necessarily mean solving their technical problems for them<sup>3</sup>.</p>

<p>What is wrong with asking questions in a native language if on a local channel or face to face with local people? Nothing. There is absolutely nothing wrong with that. It would be silly to speak another language there. But if you really want to improve, you can tap into a greater source of information. It is your choice. Pseudo-communities will only take you so far. Because <strong>they are only generating more newbies, and not supporting newbies become experienced users</strong>. Do they produce anything but empty talk?</p>

<p>Why should a local community produce anything? What should the product be? I will probably write another post about this soon. But feel free to post your ideas and critics in the comments.</p>

<hr />

<p><strong>1</strong>: Obviously, it only makes sense if your native language is not English.</p>

<p><strong>2</strong>: I would very much like to be proven wrong on this one. If you know any, please write a comment and don&#8217;t forget to include a website and date of the last meeting.</p>

<p><strong>3</strong>: Also note that there is a difference between <em>helping someone solve a problem</em> and <em>solving the problem</em>. Spoonfeeding does more harm than good.</p>


<p>Related posts:<ol><li><a href='http://www.muhuk.com/2009/05/nominate-qooxdoo-for-sourceforge-community-choice-awards/' rel='bookmark' title='Permanent Link: Nominate Qooxdoo for SourceForge Community Choice Awards'>Nominate Qooxdoo for SourceForge Community Choice Awards</a></li>
<li><a href='http://www.muhuk.com/2009/06/psychic-irc-support-in-10-easy-steps/' rel='bookmark' title='Permanent Link: Psychic IRC Support In 10 Easy Steps'>Psychic IRC Support In 10 Easy Steps</a></li>
<li><a href='http://www.muhuk.com/2009/05/freedays09-videos/' rel='bookmark' title='Permanent Link: Freedays&#8217;09 Videos'>Freedays&#8217;09 Videos</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.muhuk.com/2010/04/why-not-to-localize-community-support/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Free Software &amp; Linux Days 2010</title>
		<link>http://www.muhuk.com/2010/03/free-software-linux-days-2010/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=free-software-linux-days-2010</link>
		<comments>http://www.muhuk.com/2010/03/free-software-linux-days-2010/#comments</comments>
		<pubDate>Mon, 29 Mar 2010 07:11:39 +0000</pubDate>
		<dc:creator>Atamert Ölçgen</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[Personal]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[event]]></category>
		<category><![CDATA[free software]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[presentation]]></category>

		<guid isPermaLink="false">http://www.muhuk.com/?p=359</guid>
		<description><![CDATA[Free Software &#38; Open Source Days of İstanbul Bilgi University and Linux &#38; Free Software Festival of Linux Users Association are united under the name Free Software &#38; Linux Days this year. If you have attended before, you will probably make no other plans for April 2-3. If you have never been to this event, [...]]]></description>
			<content:encoded><![CDATA[<p><object width="480" height="385"><param name="movie" value="http://www.youtube.com/v/Aex8Kj-QYrA&#038;hl=en_US&#038;fs=1&#038;"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/Aex8Kj-QYrA&#038;hl=en_US&#038;fs=1&#038;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"></embed></object></p>

<p><em>Free Software &amp; Open Source Days</em> of İstanbul Bilgi University and <em>Linux &amp; Free Software Festival</em> of Linux Users Association are united under the name <a href="http://www.ozguryazilimgunleri.org/index_en.html"><strong>Free Software &amp; Linux Days</strong></a> this year. If you have attended before, you will probably make no other plans for April 2-3.</p>

<p>If you have never been to this event, registration is free and can be done at the front desk. If you are remotely interested in free software or hackerdom you will want to be there. &#8230;and, of course, you are welcome.</p>

<p>I will be giving a <a href="http://www.ozguryazilimgunleri.org/program.html#atamert_olcgen">Django presentation</a> on Friday. Please come and say hello if you happen to be attending.</p>

<p><strong>UPDATE:</strong> You can find the slides from presentation <a href="http://www.slideshare.net/muhuk/django-ile-arsz-tehis-ve-tedavi">here</a>. Slideshare&#8217;s importer failed to import the file I&#8217;ve uploaded properly. So please download and view the slides with Acrobat Reader.</p>

<div style="width:425px" id="__ss_3613121"><strong style="display:block;margin:12px 0 4px"><a href="http://www.slideshare.net/muhuk/django-ile-arsz-tehis-ve-tedavi" title="Django ile Ağrısız Teşhis ve Tedavi">Django ile Ağrısız Teşhis ve Tedavi</a></strong><object width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=freedays2010djangoileagrisizteshisvetedavi-100401090518-phpapp02&#038;stripped_title=django-ile-arsz-tehis-ve-tedavi" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=freedays2010djangoileagrisizteshisvetedavi-100401090518-phpapp02&#038;stripped_title=django-ile-arsz-tehis-ve-tedavi" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed></object><div style="padding:5px 0 12px">View more <a href="http://www.slideshare.net/">presentations</a> from <a href="http://www.slideshare.net/muhuk">muhuk</a>.</div></div>


<p>Related posts:<ol><li><a href='http://www.muhuk.com/2009/05/django-formfieldset/' rel='bookmark' title='Permanent Link: django-formfieldset'>django-formfieldset</a></li>
<li><a href='http://www.muhuk.com/2010/03/whats-new-in-django-formfieldset-1-1/' rel='bookmark' title='Permanent Link: What&#8217;s New in django-formfieldset 1.1'>What&#8217;s New in django-formfieldset 1.1</a></li>
<li><a href='http://www.muhuk.com/2009/04/freedays09-seminar-program/' rel='bookmark' title='Permanent Link: Freedays&#8217;09 Seminar Program'>Freedays&#8217;09 Seminar Program</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.muhuk.com/2010/03/free-software-linux-days-2010/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>What&#8217;s New in django-formfieldset 1.1</title>
		<link>http://www.muhuk.com/2010/03/whats-new-in-django-formfieldset-1-1/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=whats-new-in-django-formfieldset-1-1</link>
		<comments>http://www.muhuk.com/2010/03/whats-new-in-django-formfieldset-1-1/#comments</comments>
		<pubDate>Sat, 20 Mar 2010 20:40:04 +0000</pubDate>
		<dc:creator>Atamert Ölçgen</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[django-formfieldset]]></category>
		<category><![CDATA[forms]]></category>
		<category><![CDATA[free software]]></category>
		<category><![CDATA[open source]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[share]]></category>

		<guid isPermaLink="false">http://www.muhuk.com/?p=352</guid>
		<description><![CDATA[I have just released 1.1 version of django-formfieldset. I has been almost a year since version 1.0. Here is a summary of changes for this version: New Example Project There is a new and improved example project. It is designed to be some sort of documentation at the same time. When you run the example [...]]]></description>
			<content:encoded><![CDATA[<p>I have just released 1.1 version of <a href="http://github.com/muhuk/django-formfieldset/">django-formfieldset</a>. I has been almost a year since <a href="http://www.muhuk.com/2009/05/django-formfieldset/">version 1.0</a>. Here is a summary of changes for this version:</p>

<h3>New Example Project</h3>

<p>There is a new and improved example project. It is designed to be some sort of documentation at the same time. When you run the example project and visit different pages you will see, for each examle, Python code, template code, text of rendered result and finally the result embedded.</p>

<p>If you have <a href="http://pygments.org/">Pygments</a> installed all the code will be nicely highlighted.</p>

<h3>Fieldset &amp; FieldsetMixin Improvements</h3>

<p>Fieldset definitions are validated now. An exception will be raised if all of your fields are not included exactly once.</p>

<p>Template strings that are used by <code>as_table</code>, <code>as_p</code> and <code>as_ul</code> methods are now class attributes. You can simply override them instead of writing your own <code>as_*</code> method.</p>

<p><code>FieldsetMixin</code> provides a <code>fieldset_dict</code> attribute. This dictionary has <strong>slugified</strong> fieldset names as keys and <code>Fieldset</code> instances as values. Your fieldset declarations can still be accessed from <code>fielsets</code> attribute.</p>

<h3>Rendering Improvements</h3>

<p>There are two rendering related improvements: individual fieldset rendering and <code>renderform</code> template filter.</p>

<p><code>Fieldset</code> objects have <code>as_table</code>, <code>as_p</code>, <code>as_ul</code> methods just like forms. Errors from hidden fields are handled correctly, but you still need to call <code>non_field_errors()</code> to get the top level errors. Also it is template author&#8217;s responsibility to make sure all the fieldsets are rendered.</p>

<p>If <code>as_*</code> methods are not enough for you, with <code>renderform</code> filter you can render your forms or <code>Fieldset</code>s through a custom template. It works like this:</p>

<pre><code>{{ form.fieldset_dict.mytitlerenderform:"myapp/mytitle_fieldset.html" }}
</code></pre>

<p>If you call it without an argument <code>formfieldset/form.html</code> template will be used.</p>


<p>Related posts:<ol><li><a href='http://www.muhuk.com/2009/05/django-formfieldset/' rel='bookmark' title='Permanent Link: django-formfieldset'>django-formfieldset</a></li>
<li><a href='http://www.muhuk.com/2009/07/django-renderformplain/' rel='bookmark' title='Permanent Link: django-renderformplain'>django-renderformplain</a></li>
<li><a href='http://www.muhuk.com/2010/01/dynamic-translation-apps-for-django/' rel='bookmark' title='Permanent Link: Dynamic Translation Apps for Django'>Dynamic Translation Apps for Django</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.muhuk.com/2010/03/whats-new-in-django-formfieldset-1-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The Zen of CherryPy</title>
		<link>http://www.muhuk.com/2010/03/the-zen-of-cherrypy/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=the-zen-of-cherrypy</link>
		<comments>http://www.muhuk.com/2010/03/the-zen-of-cherrypy/#comments</comments>
		<pubDate>Sun, 14 Mar 2010 12:25:03 +0000</pubDate>
		<dc:creator>Atamert Ölçgen</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[cherrypy]]></category>
		<category><![CDATA[server]]></category>
		<category><![CDATA[share]]></category>
		<category><![CDATA[web development]]></category>
		<category><![CDATA[zen]]></category>

		<guid isPermaLink="false">http://www.muhuk.com/?p=349</guid>
		<description><![CDATA[An awesome presentation by Robert Brewer from PyCon 2010. Make sure you watch at a comfortable time. It&#8217;s good to see CherryPy project is pretty much alive. It&#8217;s a web server and a web framework and more. Check it out if you haven&#8217;t! Related posts:Freedays&#8217;09 Videos Nominate Qooxdoo for SourceForge Community Choice Awards Psychic IRC [...]]]></description>
			<content:encoded><![CDATA[<p>An awesome presentation by <a href="http://www.aminus.org/blogs/index.php?blog=2">Robert Brewer</a> from PyCon 2010. Make sure you watch at a comfortable time.</p>

<p><embed src="http://blip.tv/play/g4VigczNJAI%2Em4v" type="application/x-shockwave-flash" width="480" height="350" allowscriptaccess="always" allowfullscreen="true"></embed></p>

<p>It&#8217;s good to see <a href="http://www.cherrypy.org/">CherryPy</a> project is pretty much <a href="http://www.cherrypy.org/timeline?from=03%2F14%2F10&amp;daysback=90&amp;changeset=on&amp;update=Update">alive</a>. It&#8217;s a web server and a web framework and more. Check it out if you haven&#8217;t!</p>


<p>Related posts:<ol><li><a href='http://www.muhuk.com/2009/05/freedays09-videos/' rel='bookmark' title='Permanent Link: Freedays&#8217;09 Videos'>Freedays&#8217;09 Videos</a></li>
<li><a href='http://www.muhuk.com/2009/05/nominate-qooxdoo-for-sourceforge-community-choice-awards/' rel='bookmark' title='Permanent Link: Nominate Qooxdoo for SourceForge Community Choice Awards'>Nominate Qooxdoo for SourceForge Community Choice Awards</a></li>
<li><a href='http://www.muhuk.com/2009/06/psychic-irc-support-in-10-easy-steps/' rel='bookmark' title='Permanent Link: Psychic IRC Support In 10 Easy Steps'>Psychic IRC Support In 10 Easy Steps</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.muhuk.com/2010/03/the-zen-of-cherrypy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Developing Reusable Django Apps: Signals</title>
		<link>http://www.muhuk.com/2010/03/developing-reusable-django-apps-signals/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=developing-reusable-django-apps-signals</link>
		<comments>http://www.muhuk.com/2010/03/developing-reusable-django-apps-signals/#comments</comments>
		<pubDate>Thu, 04 Mar 2010 08:49:17 +0000</pubDate>
		<dc:creator>Atamert Ölçgen</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[reusable]]></category>
		<category><![CDATA[signals]]></category>

		<guid isPermaLink="false">http://www.muhuk.com/?p=346</guid>
		<description><![CDATA[I wrote &#8220;signals provide a great way to propagate the events generated from your app&#8221; earlier. I think reusable apps should avoid hardcoding any kind of event handling and send signals instead. App consumers might prefer an email over an on-screen notification. They may even choose to ignore the event silently. A reusable app should [...]]]></description>
			<content:encoded><![CDATA[<p>I wrote &#8220;<a href="http://www.muhuk.com/2010/01/developing-reusable-django-apps/"><em>signals provide a great way to propagate the events generated from your app</em></a>&#8221; earlier. I think reusable apps should avoid hardcoding any kind of event handling and <a href="http://docs.djangoproject.com/en/dev/topics/signals/">send signals</a> instead. App consumers might prefer an email over an on-screen notification. They may even choose to ignore the event silently. A reusable app should give this choice to the consumer.</p>

<p>Taking advantage of signals doesn&#8217;t necessarily mean providing no sane defaults. You can send signals <strong>and</strong> provide default event handling. Here is a couple of ideas how this can be done:</p>

<ul>
<li>Your app can <a href="http://stackoverflow.com/questions/2209159/disconnect-signals-for-models-and-reconnect-in-django/2209804#2209804">check if there are any listeners</a> and connect the default handlers if there is none.</li>
<li>You can ship an auxiliary app that connects default handlers when added to <code>INSTALLED_APPS</code>.</li>
</ul>

<p>I personally prefer the second approach since it&#8217;s simpler and more explicit. I&#8217;m sure there are other ways to implement default handlers for signals.</p>

<h3>Dispatch_uid</h3>

<p>Don&#8217;t forget to assign a unique <code>dispatch_uid</code> for each <code>connect()</code> call. Otherwise your handler can get <a href="http://code.djangoproject.com/wiki/Signals#Helppost_saveseemstobeemittedtwiceforeachsave">connected twice</a>. I would also suggest you to use both module path <strong>and</strong> your handler function&#8217;s name in your <code>dispatch_uid</code>:</p>

<pre><code>"%s.%s" % (os.path.splitext(__file__)[0].replace(os.sep, '.')[1:],
           handler_name)
</code></pre>

<p>Now I should take my own advice and replace hardcoded <code>User.message_set.create()</code>s with signals in <a href="http://github.com/muhuk/django-simple-friends">django-simple-friends</a>.</p>


<p>Related posts:<ol><li><a href='http://www.muhuk.com/2010/01/developing-reusable-django-apps/' rel='bookmark' title='Permanent Link: Developing Reusable Django Apps'>Developing Reusable Django Apps</a></li>
<li><a href='http://www.muhuk.com/2010/01/developing-reusable-django-apps-app-settings/' rel='bookmark' title='Permanent Link: Developing Reusable Django Apps: App Settings'>Developing Reusable Django Apps: App Settings</a></li>
<li><a href='http://www.muhuk.com/2010/01/dynamic-translation-apps-for-django/' rel='bookmark' title='Permanent Link: Dynamic Translation Apps for Django'>Dynamic Translation Apps for Django</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.muhuk.com/2010/03/developing-reusable-django-apps-signals/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Web Site Performance Optimizations</title>
		<link>http://www.muhuk.com/2010/02/web-site-performance-optimizations/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=web-site-performance-optimizations</link>
		<comments>http://www.muhuk.com/2010/02/web-site-performance-optimizations/#comments</comments>
		<pubDate>Mon, 22 Feb 2010 12:01:43 +0000</pubDate>
		<dc:creator>Atamert Ölçgen</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[optimization]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://www.muhuk.com/?p=342</guid>
		<description><![CDATA[Recently I have done some optimizations to make telvee a little faster using django_compressor and making sprites for background images. Good news is substantial changes to development environment and the design wasn&#8217;t required. I&#8217;ll get into details below. But first I&#8217;d like to write about the theory a little bit. I follow (and read with [...]]]></description>
			<content:encoded><![CDATA[<p>Recently I have done some optimizations to make <a href="http://www.telvee.com">telvee</a> a little faster using <a href="http://github.com/muhuk/django_compressor">django_compressor</a> and making sprites for background images. Good news is substantial changes to development environment and the design wasn&#8217;t required. I&#8217;ll get into details below. But first I&#8217;d like to write about the theory a little bit.</p>

<p>I follow (and read with great interest) Steve Souders&#8217;s blog <a href="http://www.stevesouders.com/blog/">High Performance Web Sites</a>. I must admit I was sceptical about it at first; spriting images, individual different loading behaviours of browsers&#8230; I thought they were <a href="http://en.wikipedia.org/wiki/Program_optimization">premature optimization</a>s. But I realized I was wrong as I continued to read. Steve Souders is an expert on high performance web sites and what he preaches are realistic techniques, backed by test results most of the time. If you are not following, I suggest you add it to your RSS reader.</p>

<h3>Optimization Techniques</h3>

<p>We can explore the techniques in two main categories:</p>

<ol>
<li>Techniques to reduce the data to be transferred.

<ul>
<li><strong>Minifying</strong>: Minification is removing comments and unneeded whitespace in CSS and JavaScript files. Compilers like <a href="developer.yahoo.com/yui/compressor/">YUI Compressor</a> and <a href="code.google.com/closure/">Closure</a> modifies JavaScript code to compress even further, without any changes to the functionality.</li>
<li><strong>Gzipping</strong>: Web browsers accept <a href="http://en.wikipedia.org/wiki/Gzip#Other_uses">gzip</a> encoded content for a long time. I have just compressed a 13 KB text file down to 4 KB. A two thirds compression ratio is not bad at all.</li>
</ul></li>
<li>Techniques to reduce the number of HTTP requests.

<ul>
<li><strong>Combining</strong>: CSS and JavaScript files can be combined together into a single file and therefore a single HTTP request. Gzip may be more efficient on these files. In a similar way background images can be merged into a <a href="http://en.wikipedia.org/wiki/Sprite_%28computer_graphics%29#Application">sprite</a> and then reconstructed again using their coordinates on that sprite.</li>
<li><strong>Data URI&#8217;s</strong>: Images (or other file types) can be embedded into CSS (or JavaScript or HTML) using <a href="http://en.wikipedia.org/wiki/Data_URI_scheme"><code>data: URI</code>&#8216;s</a>. Extra HTTP requests for those resources can be avoided this way.</li>
</ul></li>
</ol>

<p>You might think <em>it&#8217;s fine to perform all these optimizations, but what happens when I want to make some changes to my combined, minified JavaScript file?</em> Instead of applying these techniques blindly, it&#8217;s best to follow a sensible plan for implementing these optimizations:</p>

<ul>
<li>First of all <em>everything that can be automated should be automated</em>. Regarding the example above script files should stay uncombined and uncompressed in the development environment and optimizations should be applied when the application is published. Taking it a step further we can have the application detect changes in those files and update optimized versions automatically. (django&#95;compressor works this way)</li>
<li>I was worried that spriting would complicate managing the design. But I have seen, on the contrary; if images each sprite contain are choosen carefully it makes the process easier. Start combining images that belong to the same design element. Avoid complex arrangements, stick with horizontal or vertical stacking as much as possible. Don&#8217;t forget to leave transparent spaces between items and sprite border. Try to combine images that are loaded on the same page, avoid loading a sprite for only half of it&#8217;s elements. Don&#8217;t force yourself to combine all images, if you follow the guidelines I have mentione they won&#8217;t.</li>
<li>When performing optimizations don&#8217;t forget to use easily available tools. You can use <a href="http://developer.yahoo.com/yslow/">YSlow</a> for general analysis, <a href="http://spriteme.org/">SpriteMe</a> for image combining tips, <a href="http://compressorrater.thruhere.net/">CompressorRater</a> to compare different compilers&#8217; performance on your scripts. I would like to note that Steve Souders is the developer of first two.</li>
</ul>

<h3>Telvee Results</h3>

<p>I didn&#8217;t think about performance at all when I started developing <a href="http://www.telvee.com">telvee</a>. Too many CSS files and too many images were being loaded. Here is what it looked like before optimizations:</p>

<table>
  <tr><th>&nbsp;</th><th># of requests</th><th>load (KB)</th></tr>
  <tr><th>Homepage</th><td>25</td><td>~85</td></tr>
  <tr><th>Cup Detail</th><td>48</td><td>~80</td></tr>
</table>

<p>Then I have installed and configured <a href="http://github.com/muhuk/django_compressor">django_compressor</a>. I used YUI Compressor for both JavaScript and CSS. I have created sprites and modified CSS files manually<sup>1</sup>. Then I deployed these changes and measured again:</p>

<table>
  <tr><th>&nbsp;</th><th># of requests</th><th>load (KB)</th></tr>
  <tr><th>Homepage</th><td>12</td><td>~70 (~160)</td></tr>
  <tr><th>Cup Detail</th><td>14</td><td>~64</td></tr>
</table>

<p>In the load column of Homepage, the number in parens is the actual load. But the design of homepage is changed with this upgrade and a new 90 KB image is being loaded now. So I have accepted 70 KB in my calculations. The result of optimizations are as follows:</p>

<table>
  <tr><th>&nbsp;</th><th># of requests</th><th>load (KB)</th></tr>
  <tr><th>Homepage</th><td>52%</td><td>17%</td></tr>
  <tr><th>Cup Detail</th><td>70%</td><td>19%</td></tr>
</table>

<h3>Django&#95;compressor and Data URI&#8217;s</h3>

<p>Django&#95;compressor, developed by Christian Metts, helps you to apply optmizations I have mentioned above easily to your <a href="http://www.djangoproject.com/">Django</a> projects. You can see my fork <a href="http://github.com/muhuk/django_compressor">here</a> where I have merged some other branches and added a little bit of code myself.</p>

<p>Using <code>compressor.filters.datauri.CssDataUriFilter</code> in <code>data-uri</code> branch of this repository, you can embed linked files within your CSS files. It will only embed files less than or equal to 1024 Bytes (1 KB) by default. You can change this limit by setting <code>COMPRESS_DATA_URI_MIN_SIZE</code> in your <code>settings.py</code>.</p>

<p>There are a couple of things to pay attention when you convert your references to <code>data: URI</code>s. Firstly file contents are <code>base64</code> encoded which means approximately one third increase in size. It&#8217;s up to you to balance between increased bandwidth and reduced request counts<sup>2</sup>. Another thing to watch for is multiple references to the same file will end up embedding the same data many times. The solution to this problem is to reduce all references to one<sup>3</sup> but this might break your CSS arrangement strategy.</p>

<p>Please test django&#95;compressor&#8217;s <code>data: URI</code> support and tell me what you think. If you haven&#8217;t applied optimizations I mentioned above, you should. Thanks to django&#95;compressor they are quite easy to implement on Django projects.</p>

<hr />

<p><strong>1</strong>: I would like to add automatic sprite building/linking support to django&#95;compressor sometime.</p>

<p><strong>2</strong>: With Today&#8217;s modern connections 1~2 KB increase is a good price for 1 less HTTP request..</p>

<p><strong>3</strong>: <a href="http://meiert.com/en/blog/20090401/why-css-needs-no-variables/">http://meiert.com/en/blog/20090401/why-css-needs-no-variables/</a></p>


<p>Related posts:<ol><li><a href='http://www.muhuk.com/2009/05/sad-state-of-web-development-industry-in-turkiye/' rel='bookmark' title='Permanent Link: Sad State of Web Development Industry in Türkiye'>Sad State of Web Development Industry in Türkiye</a></li>
<li><a href='http://www.muhuk.com/2009/09/django-fixtures/' rel='bookmark' title='Permanent Link: Django Fixtures'>Django Fixtures</a></li>
<li><a href='http://www.muhuk.com/2010/01/dynamic-translation-apps-for-django/' rel='bookmark' title='Permanent Link: Dynamic Translation Apps for Django'>Dynamic Translation Apps for Django</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://www.muhuk.com/2010/02/web-site-performance-optimizations/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
