Magento Part 4 - Application Tuning

<< Back to Magento Performance Tips for Scalability Homepage


Flat Products/Categories

Enabling flat products and categories within Magento admin makes your website run faster (It allows your Magneto application to do less processing on the front-end).

It's really simple to enable.

In "System -> config -> catalog -> catalog -> frontend"

Enable flat products and categories, and clear your Magento cache.

Merge CSS/Javascript

In "System -> config -> developer" (down the bottom of the page) you can choose to merge your Javascript and CSS files. This can reduce your file requests by 10-30 requests per page load. It's simple to do and you will be able to support a lot more users on your website.

Magento Indexing

This is a blog post in itself (but I will try and be brief). Magento's indexing is such a pain! But it is there to make your site run faster (much faster, it's just unfortunate that their built-in indexing processes are super slow)!

If you are going to be running multiple stores with more than a few thousand products you are going to want to use an asynchronous indexer. I haven't used this extension below but it should give you huge improvements:

In the development team I was leading we wrote our own Magento extension that allowed us to run the indexing in the background with multiple processes running at one time. Instead of indexing 80,000 products one at a time, we can index 1,000 products individually but have 80 threads running which dramatically reduces indexing time.

Indexing (especially the 'Catalog URL Rewrite' was taking over 2.5 hours to complete). It now takes less than 1 minute to run!

If you want me to connect you with the developer who built this amazing extension let me know.

Enable Magento Compiler

Magento has a feature called 'compilation' in 'System -> tools -> compilation' that essentially compiles all files to run Magento so a single include path can be used.

Using the compiler can increase your website performance by more than 25%.

Disable modules you are not using

Magento has a bunch of built in modules that you most likely are not using, but are costing you CPU and Memory resources on your servers.

Go to 'System -> config -> advanced -> Advanced' and simply disable the modules you are not using.

Modules I have disabled in the past include:
  • Mage_Authorizenet
  • Mage_Captcha
  • Mage_Downloadable
  • Mage_Poll
  • Mage_Rating
  • Mage_tag
  • Phonenix_Moneybookers
Thoroughly test your site if you choose to disable any modules.

Set config at a website level

Not really a performance tip, but more of a recommendation if you plan on having multiple stores on your Magento installation.

Before you set any 'default' values within Magento 'System -> config', think to yourself,

"Will this setting apply to every site I run on this Magento installation?"

If it doesn't, drill down to the site config level and override the config there. Even if you are only running 1 store at the moment, try to think about future plans. If you set every config variable at the 'default' level now, if you ever need to install multiple sites you may have to go through and adjust a lot of config variables (which most likely means re-testing your entire website again). Some config options to think about are:
  • Timezone
  • Themes
  • Base URLs
  • Category display (list/grid)
  • Category pagination
  • Customer configuration
  • Facebook configuration

Block bad crawlers?

Magento can really struggle if you are getting hit by a lot of crawlers. Not that I really recommend blocking access to your site from crawlers, but there are scenarios where it may make sense. We have a client that runs a large Australian based e-commerce shop. Their site was being crawled by a bunch of Russian, Chinese and backlink bots that offered no benefit to them. In this case it made sense to block some of those bots (within the .htaccess file) rather than add 1-2 new servers to their cluster.

Below is an example of a few lines you can add to the bottom of your .htaccess file to block some bots (I have remove a bunch of lines so it wasn't a massive file - if you google bad bots you will be able to find the list).

# Block Bad Bots & Scrapers
# -----------------------------------
SetEnvIfNoCase User-Agent "^AhrefsBot" bad_bot
SetEnvIfNoCase User-Agent "Aboundex" bad_bot
SetEnvIfNoCase User-Agent "80legs" bad_bot
SetEnvIfNoCase User-Agent "360Spider" bad_bot
… I have removed a few hundred lines from here …
SetEnvIfNoCase User-Agent "^Xenu" bad_bot
SetEnvIfNoCase User-Agent "^Zeus" bad_bot
SetEnvIfNoCase User-Agent "ZmEu" bad_bot
SetEnvIfNoCase User-Agent "^Zyborg" bad_bot

# Vulnerability Scanners
SetEnvIfNoCase User-Agent "Acunetix" bad_bot
SetEnvIfNoCase User-Agent "FHscan" bad_bot

# Aggressive Chinese Search Engine
SetEnvIfNoCase User-Agent "Baiduspider" bad_bot

# Aggressive Russian Search Engine
SetEnvIfNoCase User-Agent "Yandex" bad_bot

<Limit GET POST HEAD>
    Order Allow,Deny
    Allow from all

    Deny from env=bad_bot
</Limit>

You can also block using robots.txt file (but some robots do not honor robots.txt file).

Part 5 - Magento Bulk Importing

Magento Part 3 - MySQL Setup & Performance

<< Back to Magento Performance Tips for Scalability Homepage


MySQL Master/Slave

As mentioned in Part 1 - Infrastructure & Hosting post, you need to take advantage of the MySQL master/slave support in Magento.

Gone are the days of running a single MySQL server for websites. MySQL's Master/slave replication is a great way leverage the power of multiple MySQL servers with very little effort.

Once you have setup your RDS (AWS database instances) as detailed in Part 1 - Infrastructure & Hosting, make sure your local.xml config file looks similar to the below (replace with your database connection details).

<resources>
    <db>
        <table_prefix><![CDATA[]]></table_prefix>
    </db>
    <default_setup>
        <connection>
            <host><![CDATA[RDS_HOST_MASTER:3306]]></host>
            <username><![CDATA[USER]]></username>
            <password><![CDATA[PASSWORD]]></password>
            <dbname><![CDATA[DATABASE]]></dbname>
            <initStatements><![CDATA[SET NAMES utf8]]></initStatements>
            <model><![CDATA[mysql4]]></model>
            <type><![CDATA[pdo_mysql]]></type>
            <pdoType><![CDATA[]]></pdoType>
            <active>1</active>
        </connection>
    </default_setup>
    <default_read>
        <connection>
            <use/>
            <host><![CDATA[RDS_HOST_REPLICA:3306]]></host>
            <username><![CDATA[USER]]></username>
            <password><![CDATA[PASSWORD]]></password>
            <dbname><![CDATA[DATABASE]]></dbname>
            <type><![CDATA[pdo_mysql]]></type>
            <model><![CDATA[mysql4]]></model>
            <pdoType><![CDATA[]]></pdoType>
            <initStatements>SET NAMES utf8</initStatements>
            <active>1</active>
        </connection>
    </default_read>
</resources>

With this basic setup, Magento will push all 'READ' queries to your slave database, and all of the writes and critical reads to your master MySQL server.

Tune MySQL

If you have just released a Magento site and it's not performing don't loose hope, you most likely need to tune MySQL a little to perform better.

There is this great script you can run on your server which helps identify issues you may need to fix. You can find it here:
http://turnkeye.com/blog/magento-performance-optimize-mysql/

Here are some of the config changes 1 usually make to MySQL servers:
key_buffer                 = 16M
max_allowed_packet         = 16M
thread_stack               = 192K
thread_cache_size          = 8
max_connections            = 120
query_cache_limit          = 1M
query_cache_size           = 48M
table_open_cache           = 3000
Part 4 - Magento Application Tuning

Magento Part 2 - Prepare for Scalability

<< Back to Magento Performance Tips for Scalability Homepage


Use a continuous integration/deployment server

Whenever you deploy a Magento store, there are always a bunch of scripts/processes that you will need to run to ensure the environment is setup and running correctly (you really don't want to be doing this manually on each release). I use Capistrano for all code deployments - it's lightweight and relatively easy to setup. Below are some of the tasks the deployment process does:
  • Swaps in production or staging configuration files (discussed below).
  • Clears filecache.
  • Clears memcache.
  • Clears varnish cache.
  • Removes previous builds.
  • Sets correct file permissions on directories (for uploads/imports etc...).

How to setup

deploy.rb

set :application, "APPLICATION_NAME"
set :scm, :git
set :repository, "YOUR_GIT_REPOSITORY"
set :user, "USER"
set :use_sudo, false
set :deploy_via, :remote_cache
set :copy_exclude, ['.git']
set :ssh_options, {:forward_agent => true}
set :keep_releases, 5

set :stages, ["staging", "production"]
set :default_stage, "staging"

default_run_options[:pty] = true

namespace :cache do
  desc "Clear Magento cache\nUsage: cap [stage] cache:clear -s type=[all|image|data|stored|js_css|files]"
  task :clear do
    if type.nil? || type.empty? || type == "all"
      cache_type = "all"
    else
      cache_type = "--clean #{type}"
    end
    run "if [ -e '#{current_path}/magento/shell/clearCache.php' ]; then cd #{current_path}/magento/shell && php clearCache.php -- #{cache_type}; else rm -rf #{current_path}/magento/var/cache/* ; fi "
  end

  task :flush do
    run "cd #{current_path}/magento/shell && php cleanCache.php -- flush"
  end

  task :varnish do
    #run "if [ -e '#{current_path}/magento/shell/varnish.php' ]; then cd #{current_path}/magento/shell && php varnish.php -- apply; fi "
  end
end

# if you want to clean up old releases on each deploy uncomment this:
after "deploy:restart", "deploy:cleanup"
after "deploy:restart", "cache:varnish"
after "deploy:restart", "cache:flush"

deploy/production.rb

server "localhost", :app, :web, :db, :primary => true
set :deploy_to, "DEPLOY_DIRECTORY"
set :branch, "master"

namespace :deploy do
  task :restart, :roles => :web do
    # Copy production config into local.xml
    run "cp #{ current_path }/magento/app/etc/local.xml.production #{ current_path }/magento/app/etc/local.xml"
    run "cp #{ current_path }/magento/errors/local.xml.sample #{ current_path }/magento/errors/local.xml"
    #run "cp #{ current_path }/magento/downloader/connect.production.cfg #{ current_path }/magento/downloader/connect.cfg"
    run "cp #{ current_path }/varnish/default.vcl.production #{ current_path }/varnish/default.vcl"
    run "cp #{ current_path }/varnish/secret.production #{ current_path }/varnish/secret"
    run "chmod 755 #{ current_path }/magento"
    run "chmod 755 #{ current_path }/magento/media"
    run "mkdir -p #{ current_path }/magento/media/catalog/product"
    run "mv #{ current_path }/magento/robots.txt.production #{ current_path }/magento/robots.txt"
    run "php -f #{ current_path }/magento/shell/compiler.php -- compile"
  end
end
Now checkout this repository onto your production servers.

To setup

Run the following command in the root directory:
cap deploy:setup

To deploy

Run:
cap production deploy

Support for multiple environments

Magento is a bit of a pain in supporting multiple environments (development, testing, production). But it can be easily achieved (you should be using a deployment server as mentioned above).
In /app/etc/ directory you can setup something like:

Config files

  • local.xml.development.example (Example config for dev)
  • local.xml.development (use .gitignore so this file is not checked in)
  • local.xml.staging (For staging environment)
  • local.xml.testing (For unit testing environment)
  • local.xml.production (For production environment)
Use a .gitignore to stop people from committing in local.xml.

The continuous integration server swaps in the correct config file based off the environment (as you can see in the deploy scripts above).

System logging

Magento stores its errors in a variety of places (/var/report/, php ini error log file, apache error log file). When error logs are stored all over the place it takes longer to debug issues, and when something goes wrong, time isn't something you have on your side. If you are running Magento on multiple servers its even harder to debug!

I have built a "system_log" extension which stores every single error message in a system_log MySQL table using delayed writes (so as to have minimal impact on the live site). This database table of error logs is an aggregation of all errors from all of your Magento servers, making it so much easier to debug issues. You could look at pushing these errors to external providers if you don't have the resources in house to have higher spec servers (PaperTrailApp and Loggly are worth a look).

Another helpful feature is a simple cron that runs every minute and emails the software teams if any error messages occur (that are not debug messages). This saves someone checking the system_log table constantly. Because this is an asynchronous service, even if something goes horribly wrong and you trigger thousands of errors, you will only be sent a combined email of errors once a minute (so it wont bring your server down).

If you are interested in this 'system_log' extension send me a message.

DB migrations

You may already know this, but you don't need to be creating SQL files to manage migrations in Magento. For database/SQL migrations setup a directory:

app/code/local/ORGANISATION/MODULE/sql/ORGANISATION_MODULE_setup

Inside this directory create an SQL file to manage running migrations 

eg:
startSetup();

$installer->run("
CREATE TABLE IF NOT EXISTS {$this->getTable('system_log')} (
  `SystemLogId` int(10) NOT NULL AUTO_INCREMENT,
  `Message` text NOT NULL,
  `PriorityName` varchar(100) DEFAULT NULL,
  `PriorityLevel` int(5) DEFAULT NULL,
  `UserIp` varchar(50) DEFAULT NULL,
  `UserHost` varchar(250) DEFAULT NULL,
  `SectionId` int(10) NOT NULL DEFAULT '1',
  `Attachment` text,
  `Created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`SystemLogId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `system_log_section` (
  `SystemLogSectionId` int(10) NOT NULL AUTO_INCREMENT,
  `Name` varchar(50) NOT NULL,
  PRIMARY KEY (`SystemLogSectionId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `system_log_section` (`SystemLogSectionId`, `Name`)
VALUES
 (1,'Default'),
 (2,'Magmi');
");

$installer->endSetup();


I found very little documentation about these SQL migrations, but nearly all modules use them in the Magento world, so it's fairly easy to find some examples.

File storage, sessions and images

For any system to scale you need to have your data (or file assets in this case) stored in a central location (single server or cluster of servers). Why? Because if you move from a single server environment to a multi server environment if you are storing data like images, session files etc they will only exist on 1 server (not on all of your servers) which can be problematic.

Sessions

In Magento you should use database session storage so your users sessions persist across multiple servers. Within your local.xml file set database storage for sessions

<config>
    <global>
 <session_save>db</session_save>
    </global>
</config>

A good article about performance of different session storage options
http://magebase.com/magento-tutorials/magento-session-storage-which-to-choose-and-why/

Image Assets (product images)

I typically store all product images in the database of Magento (or a central storage platform that isn't the file system) - before you worry about performance, read on. Magento has 'partial' functionality to store images in the database but it is no-where near complete. A team I was working with ended up developing our own extension to enhance the Magento core so we had a more seamless integration with database storage for product images.

If you upload and store images on the filesystem only, with every deployment you will wipe out and delete those images. Unless you store them in a folder outside the Magento directory and symlink them in (but then you need to worry about another type of backup).

Storing all product images in the database gives you a central place where all images are stored (you can copy databases from production to testing or to development) and have everything continue to work very easily, without the need to copy images from different servers. However, most of you are probably thinking about performance, rendering images from the database is seriously slow.

The good news is, Magento automatically writes the images to the filesystem on first request. This means on first request the image may take around 1 second to load, but on every subsequent request it is being read directly from Apache and the filesystem which is very fast with no application overheads.

Go a step further and implement CDN and you have completely offloaded the product image loading process to your CDN servers and they will load within milliseconds.

To implement the above requires a custom Magento extension, so send me a message if you are interested.

CDN (Content Delivery Network)

As mentioned above, with Magento you need to offload as many resources as possible from your servers to make it perform fast. Serving all of your product images from CDN is a great way to do this.

Grab this extension to start:
http://www.magentocommerce.com/magento-connect/onepica-imagecdn-1.html

A team I worked with patched this extension to support Origin-Pull for CDN (this is a far simpler implementation of CDN, which doesn't require your application to push your content to CDN, your CDN pulls the content from your server on first request).

If you want to learn more about this extension send me a message.

Magento Part 3 - MySQL Setup & Performance

Magento Part 1 - Infrastructure & Hosting

<< Back to Magento Performance Tips for Scalability Homepage


Shared Hosting

Getting straight to the point - simply don't use it - ever :-) Magento requires tuning of your MySQL servers, your web servers, caching servers etc.. On a shared hosting environment you don't have access to these settings. Save yourself a lot of pain and setup your own infrastructure.

Recommended Infrastructure Setup

Setup your Magneto environment on AWS (Amazon Web Services). It's extremely cost effective and if you follow the below guidelines you can scale up your servers as your server load increases over time.

Route32 for DNS

Why? Because it is super simple to setup, easy to maintain, has redundancy and is really cheap!

EC2 & Load Balancer for Front-end web servers

You will want to run at least 2x EC2 front-end servers for your Magento website both sitting behind a load balancer.

"Load balancers allow you to run multiple front-end (Apache/PHP) servers for your Magento installation. They are great for performance, redundancy and for creating downtime free releases."

  1. Setup 2x medium instance web servers for your Apache/PHP/Magento codebase.
  2. You will want to use EC2 servers that have at least 2 CPUs (Magento in most cases will perform much better with more CPU power than memory).
  3. Setup the 2 servers identically (look into CloudFormation if you want to automate this).
  4. Now setup 1 load balancer for your 2 front-end servers.
  5. Add your 2 front-end servers to your load balancer (You will need to setup a polling end-point so the load balancers know the server is in service). It could be as simple as having a PHP file in the root directory like below:

    /load-balancer-status.php
    <?php var_dump($_SERVER['REMOTE_ADDR']); ?>

    In your Amazon Load Balancer your health check should look something like:
    HTTP:80/load-balancer-status.php

EC2 & Load Balancer for admin web servers

It is good practice to run your Magento admin on its own servers. This will ensure that your staff and admin users wont affect your front-end website if using Magento admin heavily. It also allows you to add a layer of security to your Magento admin (and lock it down to just your office network).
    1. Setup 2x medium instance admin web servers (these can be identical to your front-end web servers) for your Apache/PHP and magneto codebase.
    2. Again, you will want to use EC2 servers that have at least 2 CPUs.
    3. Setup the 2 servers identically.
    4. Now setup 1 new load balancer for your 2 admin servers.
    5. Add your 2 admin servers to your load balancer (like you did you front-end servers).

    RDS for your database

    You will want to run your database on a separate server to your admin and front-end servers. This will give you much better performance and decouple your application servers from your database (good for scaling).
    1. Setup 1x medium RDS instance for your database.
    2. Import your initial MySQL database dump to this database.
    3. You will want to leverage the power of MySQL replication for your Magento website, so within the RDS configuration of your database above, create a 'read replica'.
    4. This way you can share the load of your MySQL queries over both of your databases (more details about how to configure Magento for read replica in Part 3 - MySQL setup and performance).

    Elastic Cache

    Magento needs cache for it to support even just a few users efficiently. 
    1. Setup an elastic cache instance (Redis), or setup Memcache on your front-end and admin servers.
    2. Configure Magento to use this caching service
    3. More details about how to configure Magento for caching in Part 6 - Magento Caching.

    CloudFront CDN for product images

    Using a CDN allows you to offload a lot of static resources (like product images, CSS, Javascript etc...) to CDN servers all around the world. This will free up your server resources.
    1. Setup a CloudFront distribution within AWS.
    2. This will give you a URL like:
      d1u5cic2xm0cb9.cloudfront.net
      that we can configure later in Magento to serve content from.
      Part 2 explains how to setup CDN within Magento

    SES for transactional emails

    Your Magento website will send out transactional emails (contact form emails, order emails etc...). You may as well keep this on AWS with simplicity.
    1. Setup SES within AWS.
    2. You will need to configure your domain, and allowed senders.
    3. You will also have to apply for production use (which can take up to 48 hours). Make sure you do this a few days before launch.

    Reserved instances

    You can dramatically reduce your AWS costs by purchasing reserved instances. You should purchase 'heavy utilisation' reserved instances for all of your EC2 and RDS instances (your costs will be about 1/3 to 1/2 of the costs of not purchasing reserved instances).

    Part 2 - Prepare for Scalability


    Why every database table should have created/modified columns

    This is a short and sweet post about those crucial 'created and modified' database columns that can be your saviour down the line for any project.

    I'm sure almost everyone has worked on applications where the original developer missed adding a created or modified timestamp column on a database. There are many reasons for it - the developer forgot, ran out of time, or sometimes failed to realise the importance of time stamping every database record.

    The simple rule is:

    "Every table should have a column for "created timestamp" and "modified timestamp".

    There are extensions to this in regards to having an 'owner' who modified and versioning history, but that is a far greater conversation.

    The benefits you will get within your application:
    • You will always have a record of when the record was created.
    • You will always have a record of when the record was last modified.
    • It will dramatically speed up debugging of issues.
    • It provides necessary auditing information for your application.
    • Allows you to implement data archiving practices in the future.
    What can happen if you don't implement:
    • You will have no idea when records were created or modified.
    • Makes debugging issues related to times and external error logs much harder.
    • You can't recover records based off date/times for legal purposes.
    • It will be near impossible to archive data in the future.
    Gotchas:

    • Use a consistent method for timestamps. What this means is don't use native database timestamps in one area of your application (eg: 'NOW()' in SQL), and then use PHP timestamps in another area of your application (eg: new \DateTime()). This can cause inconsistencies if your application logic and database logic are on different servers in different timezones and not configured correctly. Use a single method (either PHP or in SQL) and stick with it.
    Most frameworks (eg: Symfony2, Zend) provide events you can plug into to make implementation much easier. You can even look at creating an abstract class all of your entity classes extend that provide this core functionality.

    Public and Private Methods with Javascript

    How to achieve public and private methods with Javascript

    This is just a short post on how I construct my javascript objects to provide public and private method functionality (without using a framework). As much as I enjoy Javascript there are a million ways to do very similar things - hopefully this explains one good approach to do this.

    What this approach provides you with:

    • Private methods
    • Public methods
    • Follows OOP practices

    What this approach doesn't provide you with:

    • Inheritance :-(
    • If you want inheritance I highly recommend checking out the ExtJS framework. It's a very powerful framework that promotes good coding standards and architecture.

    Why do I place high importance on using private methods/properties?

    When I write any code I aim to hide as much of the complexity of the library from the outside world. The less functionality I have to expose to the outside world, the easier it is to refactor the code in the future, as you know exactly how your class can and can't be used.

    So here is the example..

    /**
     * A javascript object to manage data about a product (eg: book, computer, car or house etc..)
     */
    var ProductInstance = function(id, name, price, discount){
    
        /**
         * ID of the product
         */
        var _id = id;
    
        /**
         * Name of the product
         */
        var _name = name;
    
        /**
         * Price of the product
         */
        var _price = price;
    
        /**
         * Discount of the product
         */
        var _discount = discount;
    
        /**
         * Private method to calculate discounted price
         */
        function _calculateDiscountedPrice() {
            var discountedPrice = _price;
    
            if( parseFloat(_discount) >  0 ) {
                discountedPrice = discountedPrice - _discount;
            }
    
            return discountedPrice;
        }
    
        /**
         * Private method to build up post data of an object
         */
        function _buildPostData() {
            var data = {}
    
            data.id = _id;
            data.name = _name;
            data.price = _price;
            data.discount = _discount;
    
            return data;
        }
    
        /**
         * Private method to save a product
         */
        function _save() {
    
            // You could save to local storage..
    
            // You could make an AJAX request to save to server..
            //$.ajax('/product/save/', _buildPostData());
        }
    
        /**
         * Public method to get Id
         */
        this.getId = function(){
            return _id;
        }
    
        /**
         * Public method to get Name
         */
        this.getName = function(){
            return _name;
        }
    
        /**
         * Public method to set name
         */
        this.setName = function(name){
            _name = name;
        }
    
        /**
         * Public method to get Price
         */
        this.getPrice = function(){
            return _price;
        }
    
        /**
         * Public method to set Price
         */
        this.setPrice = function(price){
            _price = price;
        }
    
        /**
         * Public method to get Discount
         */
        this.getDiscount = function(){
            return _discount;
        }
    
        /**
         * Public method to set Discount
         */
        this.setDiscount = function(discount){
            _discount = discount;
        }
    
        /**
         * Public method to set Discount
         */
        this.getDiscountedPrice = function(){
            return _calculateDiscountedPrice();
        }
    
        /**
         * Public method to save product
         */
        this.save = function(){
            return _save();
        }
    
        return this;
    };
    
    var productOne = new ProductInstance(1, 'Product One', 20, 5);
    console.log(productOne.getId()); // equal "1"
    console.log(productOne.getName()); // equal "Product One"
    console.log(productOne.getPrice()); // equal "20"
    console.log(productOne.getDiscount()); // equal "5"
    console.log(productOne.getDiscountedPrice()); // equal "15"
    //console.log(productOne._calculateDiscountedPrice()); // Method will not exist as method is private
    //console.log(productOne._buildPostData()); // Method will not exist as method is private
    //console.log(productOne._save()); // Method will not exist as method is private
    
    var productTwo = new ProductInstance(2, 'Product Two', 50, 30);
    console.log(productTwo.getId()); // equal "2"
    console.log(productTwo.getName()); // equal "Product Two"
    productTwo.setName("Product Two - Updated");
    console.log(productTwo.getName()); // equal "Product Two - Updated"
    console.log(productTwo.getPrice()); // equal "50"
    console.log(productTwo.getDiscount()); // equal "30"
    console.log(productTwo.getDiscountedPrice()); // equal "20"
    //console.log(productTwo._calculateDiscountedPrice()); // Method will not exist as method is private
    //console.log(productTwo._buildPostData()); // Method will not exist as method is private
    //console.log(productTwo._save()); // Method will not exist as method is private
    

    As you can see with the overly verbose example above, you can easily achieve public/private methods in Javascript. It's a very simple example, but in the real world your '_save' method could be quite complicated (even to the extend of using a different class to do the saving). Being able to hide all of that complexity from the outside world allows you to easily refactor it in the future. You can even unit test the above code quite easily as its not tightly coupled to any other components.



    Dion Beetson
    Founder of www.ackwired.com

    A Developers Resume and Interview

    Technical/Developer Resume, Interview and Coding Tests

    I've recently been involved in recruiting new software engineers to build up a team at my workplace.

    If you are looking for a new technical developer role, the post below might provide some insight into the processes you may have to work your way through.

    1. Wrestle your way through the recruitment agency

    Many companies use a recruitment agency to filter the volume of job applications, or even head hunt the developers the company is looking for. You could be one of the most talented developers around, but if you don't structure your CV correctly, you may find you are constantly being blocked at this point.

    Recruiters will basically look through your CV to ensure you meet the selection criteria of the position you are applying for. Here's what you can do to increase your odds of making it through this phase:

    • Research the selection criteria for the position you are applying for.
    • Clearly outline how you meet that selection criteria within your CV.
      Are they looking for:
      • Team mentoring?
      • Experience in building scalable web applications?
      • Test Driven Development?
      • Experience in agile methodologies?
      • Outstanding communication skills?
      • Experience in Javascript based web applications?
      • PHP developers? or Frontend Developers?
      • You really need to nail this criteria
    • Use dot points, or short sentences if you have to.
    • Including large paragraphs of information make it harder for recruiters to find exactly what they are looking for.
    • Use the correct terminology, for example:
      • If you have been helping developers in your day job, use the word 'mentoring'.
      • If you have worked on big applications, use terms like "large scale", "master/slave databases", "caching implementations" etc.. 
      • If you worked in a specific environment, was it SCRUM, Waterfall, XP?
      • Recruiters will look for keywords that match the selection criteria they are recruiting for.
    • Double, actually no, triple check your grammer, and use the spell checker. There is nothing worse than a CV with spelling mistakes.
    • Keep it short, as recruiters will most likely skim read over your CV. I know you most likely have a lot to express within your CV, but really try to outline the most important - you have your interview to explain in more detail.
    • Include the most important information up the top - most people wont read more than 2 pages.
    • Trial your CV on a few advertised positions, get feedback, refine, and try again - if you send your CV out to every recruiter in the first few days, you may live to regret it.

    2. Your potential employer reviews your CV

    So, you have impressed the recruitment agency and your CV has most likely been forwarded onto the company you applied to. The employer will go through a very similar process, with the difference being, they will most likely have a technical background (eg: technical managers or team leads). They will look more thoroughly into your CV (I still doubt they will read it word for word), but they will analyse technical ability, so it's also important your CV demonstrates your strengths and weaknesses. For example:

    • Define the coding languages your primary experienced with.
    • What databases do you have experience with?
    • What frameworks do you have experience with?
    • What version control platforms have you worked with?
    • What testing do you usually implement?
    • What type of projects you have worked on?
    • What type of project teams have you worked with?
    • Be prepared to be able to backup everything you have outlined in your CV.
    • Be prepared to share some good examples about projects you have worked on.

    3. Interviewing with your potential employer

    If you make it the the stage, you have obviously ticked all the boxes on paper - good work! In my experience the interview is usually looking to determine 2 primary objectives:

    1. Are you technically capable to work within the team?
    2. Do you fit the culture of the team (this was so important to our team).

    You really need to have a balance of both. Being technically awesome but hard to work with (arrogant or opinionated etc..) wont get you very far, being a great team player but not being able to back it up with your technical skills will most likely not get you far either.


    For your interview:
    1. Always prepare a few questions to ask your interviewees - it shows your serious about the position.
    2. Know the background of your interviewers (if you know who they are), and the company.
    3. Be prepared and practice some of the questions they might ask you.
      In my experience, these may include some of the following:
      • What design patterns would you recommend using and why?
      • How would you solve a problem you don't know the answer to?
      • What would you do if you had a blocker on a project you were working on and no one was in the office?
      • How do you test and become confident in a component you have developed?
      • What's your most challenging coding problem you have had to solve, and how did you solve it?
      • If you are given a problem you don't know how to solve, how would you solve it?
      • How do you scale a web application?
        For example.. Load balancers, master slave databases, sharding, asynchronous programming, application caching, SQL optimisation etc..

    4. Technical Coding Tests:

    This is my favourite! I really enjoy reviewing technical tests - you can learn a lot by looking through someone else's code. Unfortunately for you, some developers can be egotistic when they review - they really are looking for the perfect solution. Sometimes they may even look for similar coding styles to their own, or similar to members within their team.

    Here are some things to look out for and to include:

    • If it's a 24 hour coding challenge, spend more than an hour on it, but probably less than 16 hours. We had one developer who spent nearly 18 hours coding his technical test - it was fairly impressive though!
    • Show your knowledge of design patterns, but try to use well known ones and not patterns that the reviewer wouldn't recognize. Patterns I would look for are:
      • Dependency Injection.
      • Use of interfaces, you can type check against interfaces.
      • MVC implementation (if it was a front-end test).
      • Single use/purpose classes, don't add all of your logic into a single class.
      • OOP design, I wouldn't code up a procedural application.
      • Security awareness - ensure there is NO XSS (cross site scripting) or SQL Injection.
      • Use of class inheritance.
    • Write a few unit tests to show you at least considered it.
    • Document your code, yes document your code!
      • Use correct spelling and grammer.
    • Format your code neatly.
      A pet hate of mine (which most developers who have worked with me know about me), is when developers use a mix of TABS and SPACES for indentation. As much as I prefer SPACES over TABS in any application, consistency is the key, use TABS or SPACES, don't use both.
    • Consistent file naming, variable naming, method naming, and code formatting.
    • oh and never ever use global variables - that was pretty much an automatic fail in our books.

    Some final notes

    • Adjust your CV based on the position you are applying for.
    • If you are applying to slightly different roles, eg front-end developer or back-end developer, have a generic CV with all your skills. You can use that as a base to copy and paste into a CV targeted to the position you are applying for. Trust me you will have far more success in this approach even though it does take a little longer to apply for jobs - You really want to be able to showcase the skills the employer is looking for.
    • And finally, try not to go into the interview asking for huge amounts of money up front. Spend time proving to your interviewers that you are awesome, smart and well matched to what they are looking for - make them want you. After that is when you will have the power to ask for the salary you are worth.

    Good luck!

    Dion Beetson
    Founder of www.ackwired.com