I also deeply enhanced client side for speed. First thing was to minimize external files – CSS and JS. Minimized code strips out all unnecessary stuff, like whitespaces and comments. Deep minification also rewrites variable and method names to contain as few characters as possible. Every byte counts.
Minimized file can be even smaller using GZip compression, widely supported by browsers. This way you can 58 kB file compress down to 12 kB. Of course you keep all the source files in their original version, use them for testing and all those tweaks you do automatically during deployment to production server.
I know JS libraries/frameworks, like jQuery or Dojo, are really handy, but you should use them only if using pure (“Vanilla”) JS would be much harder or problematic. This also applies for various CSS boilerplate frameworks (Bootstrap, Foundation). Because it’s universal, it contains a lot of code you never use, but browser parses it on every page load. If you want to use it, remove all unnecessary stuff, if possible (there are tools for that). jQuery is 85 kB of code (and growing) just to make another code works. Using JS lib only for element addressing is pure blasphemy (use document.querySelector
instead) and I don’t even want to comment using more than one JS library on single page.
Next step is reducing HTTP request count. I merged all JS into single file, the same with CSS. Then I created so called “CSS sprite” – all graphics (pictures), where possible, migrate into single PNG file (“spritesheet”) and instead whole CSS background, specify just background-position. You have to do it cleverly, because of mixing repeating (backgrounds) and non-repeating images (icons). For tiny pictures you can use data URIs and put them into your CSS file. All of this may be negated by the upcoming HTTP/2, which heavily uses parallelization and therefore many small files will actually load faster, than one larger spritesheet.
One of the most difficult steps was to minimize HTML code. Some browsers (with blue “E” in logo ;-) in some previous (but still used) versions can’t handle innerHTML manipulations without another DIV, so every time I want to use element.innerHTML = someText
, I have to specify extra inner DIV. There are certain situations, when the HTML code is really bloated and I can’t do anything about it. If you write the code in HTML5, you can at least get rid of the ending slash on non-pair HTML tags, like INPUT or IMG.
It’s also good to check for invalid stuff over time. You may not use the widget or page layout any more, but linked scripts and CSS may still be out there. Also check for unused JS functions/methods and unused CSS classes.
Put your all your external scripts right above </body>
or use async/defer attributes. To HEAD of the page you can put the most basic CSS styles for rough page styling to prevent layout jumping and all other styles load from external CSS file.
Social network buttons can be trouble too, especially when they are loaded directly from that site (with Like counters etc). If you want them, make it yourself and add them to your spritesheet / icon font. This way you also don't provide tracking info to that site.
My key request for the new system was good performance, no matter how achieved. I ended up with a single data table and a lot of cache around it. In Particle Database I have a search cache and coordinate cache in MySQL and each entity in JSON file. Entity contains all own attributes and relations, plus top 5 foreign relations. Each foreign relation type to each entity type has own JSON file cache.
Activate GZIP compression.
Encourage browser caching for static resources, using Expires header, Last-Modified date or ETags in the HTTP header.
Use different URL root for your resources, because browsers on HTTP 1.x allows 4 to 8 simultaneous requests per domain, so using different domain will increase this limit. And it will be cookie free (cookies are sent even for CSS or images). There are also public CDNs for widely used resources, like jQuery or Bootstrap.
Indexing of MySQL tables is crucial, too little is bad and too much is bad as well. Best practice is to work tightly with DESCRIBE command and index columns used in JOIN and WHERE clauses.
There are several situations, where indexes can’t be used. If you perform LIKE wildcard search, only col LIKE ’abc%’
or col LIKE ’a%c%’
use indexes, but col LIKE ’%abc%’
and col LIKE %abc’
and col LIKE col2
don’t – because of the way B-Tree index is designed. Or if you have to do a string operation before comparison, e.g. UPPER(col) LIKE ’ABC’
, all rows has to be loaded. In such case consider adding a new column, with cached upper-case values.
I always checked what approach in PHP would be the fastest, especially for functionality in iteration (loop). I used simple script with a timer:
This way I tried the best approach for zero padding or type casting from string to number.