How can we assess a programmer's professionalism?

How can we assess a programmer's professionalism?

October 5, 2016

“Once upon a time there was a prince who wanted to marry a princess... He travelled all over the world to find one, but nowhere could he get what he wanted… There were princesses enough, but it was difficult to find out whether they were real ones. There was always something about them that was not as it should be.”

Hans Christian Andersen, The Princess and the Pea

A similar problem often arises in the search for an experienced programmer. Job advertisements usually generate many responses, but how can a potential employer be sure that the candidate possesses the required level of professionalism? A programmer is considered a valuable asset. Nonetheless, the number of applicants with two months' training behind them is even greater than the number of fake princesses during a period of feudal fragmentation.

When looking for a programmer at EDISON Software Development Centre, we specify the required qualification levels in the advertisement. For instance, the company may require a mid-level developer. Applicants sometimes ask questions about the differentiation of levels. There is no unified classification for levels of professional aptitude. We can, however, say that the junior level covers a beginner programmer with up to two years' experience and a mid-level programmer has more than two years of experience. The senior-level tier begins at more than five years' experience. There is also a lead programmer at the top of the hierarchy, who possesses experience in leading a group of specialists. Years of experience in the profession don't guarantee the presence of the right qualities. A programmer can churn out single-page websites for five years without ever progressing to the senior developer level. This also works the other way around; with a professional instructor and dedication to serious tasks, a programmer can reach the mid-level tier within a year.

The practice of preliminary job interviews has proved ineffective and time-consuming. The preliminary application selection stage has been delegated to an HR specialist. First, the candidate completes a questionnaire and evaluates his competence in various spheres of programming on a scale of one to five. He states how long he's been using various technologies and fills in the “Finished projects” table. This information provides a general impression about the experience of the applicant and his professional skillset. Beginners tend to overestimate themselves. For example, an applicant might evaluate his level of knowledge of Рython as four out of five and underline that he is “ready to solve any task”, but at the same time states that he has two weeks' experience with this particular programming language.

The applicant's competence is evaluated in practice. The candidate completes a test task and his level is evaluated through an analysis of the work.

First factor for assessment – time taken to complete the task

A junior developer will need a week to finish a task which a senior developer could complete in just a couple of hours. The way in which an applicant evaluates how quickly he will complete a task also tells us a lot. A junior developer looks at the task far too optimistically, tending to underestimate its complexity and violating the deadline due to lack of experience. A mid-level developer would view the task pessimistically, taking into account his experience as a junior developer. Thus he overestimates the anticipated project deadline. A senior developer is realistic. He reasonably takes the risks into account without overestimating the time frame.

Second thing to look at – quality of the coding

For several years the applicants have been asked to write a simple browser game of noughts and crosses. Depending on the vacancy, a certain programming language or technology may be specified. If a significant staff increase is planned, the candidates are usually permitted to use the tools of their preference.

There are now dozens of test tasks available for use in the selection process. EDISON testers have chosen three fragments of code (a web application request processor) written on PHP by developers of different levels, with comments added.

Let's start with an example of so-called “hodgie code”:

$user = userRequestWithPassword($_COOKIE['login'], $_COOKIE['password']);

Storing the user's login and password in cookies is a flagrant security violation. Cookies are passed on from browser to server on request (opening/refreshing of the page). This exposes the user to potential password interception.

if ($user != null) {
    if (isset($_POST['submitEdit'])) {

Site activity is conducted according to POST request parameters, using successive conditional blocks. The code is getting more complicated, becoming bulky and difficult to read. To simplify the task, experienced programmers have come up with routing methods and patterns such as MVC.

Site activity is conducted according to POST request parameters, using successive conditional blocks. The code is getting more complicated, becoming bulky and difficult to read. To simplify the task, experienced programmers have come up with routing methods and patterns such as MVC.

$deal = dealRequest($_GET['dealId']);
$connect = mysqli_connect(BAZA_SERVER, BAZA_USER, BAZA_PASSWORD, BAZA_MYSQL);
$name = mysqli_real_escape_string($connect, $_POST['name']);
$date = mysqli_real_escape_string($connect, $_POST['date']);
$insured = mysqli_real_escape_string($connect, $_POST['insured']);
$obligor = mysqli_real_escape_string($connect, $_POST['obligor']);
$countryObligor = mysqli_real_escape_string($connect, $_POST['countryObligor']);
$amount = mysqli_real_escape_string($connect, $_POST['amount']);
$currency = mysqli_real_escape_string($connect, $_POST['currency']);
$percent = mysqli_real_escape_string($connect, $_POST['percent']);
$tenor = mysqli_real_escape_string($connect, $_POST['tenor']);
$type = mysqli_real_escape_string($connect, $_POST['type']);
$responseDate = mysqli_real_escape_string($connect, $_POST['responseDate']);
$person = mysqli_real_escape_string($connect, $_POST['person']);

Instead of this cumbersome code for elementary operations, an experienced programmer will write a single-line block:

if (!empty($_FILES['upload']['name'])) {
    $path_directory = 'documents/';
    $filename = $_FILES['upload']['name'];
    if (preg_match('/[.](doc)|(docx)|(pdf)|(xls)|(jpg)$/', $_FILES['upload']['name'])) {
        $source = $_FILES['upload']['tmp_name'];
        $target = $path_directory . $filename;
        $fileName = $moved = move_uploaded_file($source, $target);
    }
}
else {
    $filename = $deal['documents'];
}

$query = "update Deals
          set name='$name', date='$date', nameOfTheInsured='$insured', nameOfTheObligor='$obligor',
          countryOfTheObligor='$countryObligor', amount='$amount',
          currencyOfTheDeal='$currency', percentToBeInsured='$percent', tenorOfTheExposure='$tenor',
          typeOfTheDeal='$type', targetResponseDate='$responseDate', nameOfTheContactPerson='$person',
          documents='$filename'
          where id='".$_GET['dealId']."'";

A GET request parameter (a line coming from the user on opening a browser page) is substituted directly into the SQL query (an address to the database). This poses a potential security threat (an SQL injection).

mysqli_query($connect, $query);
mysqli_close($connect);
header("location: http://example.com/view.php?dealId=".$_GET['dealId']);

This is an example of hard-coded URL. A page address can change, so to refer to the new address the programmer will have to find and change the data for the old URL within the code.

else if (isset($_POST['addComment'])) {
    $connect = mysqli_connect(BAZA_SERVER, BAZA_USER, BAZA_PASSWORD, BAZA_MYSQL);

Naming variables in different languages is an example of transliteration in code – a common mistake of beginner coders.

    $dealId = mysqli_real_escape_string($connect, $_GET['dealId']);
    $userId = mysqli_real_escape_string($connect, $user['id']);
    $comment = mysqli_real_escape_string($connect, $_POST['comment']);

    $query = "insert into Comments (dealRefer, userRefer, comment) values('$dealId','$userId','$comment')";
    mysqli_query($connect, $query);
    mysqli_close($connect);
    header("location: http://example.com/view.php?dealId=".$_GET['dealId']);
}

An example of a junior-level coding.

if (isset($_GET['action']) && ($_GET['action'] == 'online'))

Activity is defined based on GET request parameters in successive conditional blocks, similarly to the previous example.

{
    $document = new Document();
    $document->SetLanguage($cur_lang);
    
    if ($starter = $db->GetFByQuery("SELECT u.login FROM games g
                                     LEFT JOIN users u ON g.starter=u.id
                                     WHERE g.`invited`=$uid"))
    {           echo "<a href = '#' onclick = 'return invite(0)'>$starter " . $document->Translate(17) 
                   . "</a><br><a href = '#' onclick = 'return deny()'>" . $document->Translate(19) . "</a>";
    }   

“Echo” in coding isn't the best solution for displaying text or layout in a browser. It makes the process of changing the appearance of the site more complicated. Layout should be defined in separate template files. The same is true for JS- and CSS-insertions. Division into files is a basic requirement, and if possible they should be placed in different folders.

else
    {
         $rows = $db->GetByQuery("SELECT id, login FROM users WHERE `lastping`>" . (time() - 30) . " AND `id`<>$uid");
         if (count($rows))
             foreach ($rows as $row)
             {
                 echo "<a href = '#' onclick = 'return invite(\"$row[login]\")'>$row[login]</a><br>";
             }

Here we see a hard-coded “click” event handler. The same mistake as in the previous example is visible here. All JS should be stored in separate files.

        else
        {
            echo $document->Translate(11);
        }
    }
}
elseif (isset($_GET['action']) && ($_GET['action'] == 'creategame'))
{
...
}
elseif (isset($_GET['action']) && ($_GET['action'] == 'getfields'))
{
...
}
...

Mid-level developer code is simple and contains commentaries explaining difficult elements. The programmer uses ORM (object-relational mapping) instead of writing native queries to the base. The risk of SQL injections is much lower. OOP and MVC are used.

public function actionStatistics() {
           // Overall numbers of games and number of players taken from database.
           $GamesNumber = tableGame::model() -> count();
           $PlayersNumber = tableUser::model() -> count();
           $GeneralStatistics = array(
               'GamesNumber' => $GamesNumber,
               'PlayersNumber' => $PlayersNumber
           );
           $Player = new Player();
           // List of highest rated players taken from database.
           $dbModel = tableUser::model() -> findAllByAttributes(
               array('Enable' => 1),
               array('limit' => self::TOP_PLAYERS_LIST_SIZE, 'order' => 'Rating DESC')
           );
           // Creation of an array of summary information about the best players.
           foreach ($dbModel as $PlayerData) {
               if ($Player -> Load($PlayerData -> ID)) {
                  $PlayersList[] = clone $Player;
               }
           }
           // Loading authorised player data.
           $Player -> Load(Yii::app() -> user -> getId());
           // Summary output.
           $this -> render('statistics', array(
               'GeneralStatistics' => $GeneralStatistics,
               'PlayerData' => $Player,
               'PlayersList' => $PlayersList
           ));
}

The difference between a mid-level and a senior developer is difficult to distinguish from just a fragment of code and mainly consists in choosing the correct architectural solutions.

General criteria for evaluation are presented in the following table. The list is not limited to the examples provided.

Criterion of evaluation Junior Middle Senior
Task breakdown Successive code lines; employs copy/paste for repeated code usage. Creates reusable functions/objects which serve to solve general tasks. Uses appropriate data structures and algorithms; creates general and object-oriented code, encapsulating problem specifications.
System breakdown Incapable of considering a system more complex than one class or file. Breaks down the task and designs a system within one platform or technology. Visualises and designs complex systems with several product lines, integrates them with external systems; designs systems for maintenance: monitoring, report generation, emergency changeovers to fail-safe resources.
Communication Can't communicate thoughts/ideas; isn't very good at spelling and grammar. Is easy to understand; has good command of spelling and grammar; effective communicator. Understands and explains thoughts/designs/ideas/specifications in a clear and precise form; adjusts to the situation when communicating.
Code organisation in the file No clear organisation in the file. Methods are grouped logically and according to the challenges. Code is divided into regions; provides comprehensive commentary with reference to the source files.
Code organisation between files No distinct organisation of code; no division into files. One physical file executes one function; for instance, it serves to declare a class or to implement one kind of functionality. Organisation of the code physically corresponds to the project; the way the implementation is designed is evident in the names of files and the structure of directories.
Code readability One-syllable names. Correct names of files, parameters, classes, methods etc; absence of long functions; a non-standard code, bug fixes and assumptions in the code are explained in commentaries. Assumptions in the code are accompanied by assertion commands; the flow of operations in the code is natural (without deep nesting of conditions and methods).
Programming security Doesn't grasp the concept. Checks methods' arguments, returned values and handling of exceptions in potentially important code. Has a personal library which helps in securing programming; writes unit tests to imitate glitches.
Error handling Writes the code for an “ideal” situation. Error handling within the code (inserts an exception or generates an error). Writes the code with a function for early error detection; sticks to the successive exceptions handling in the code layers and formulates the principles of the system process.
Requirements Understands the stated requirements and writes the code according to the specification. Sees the overall picture and at once identifies some additional aspects to be later described in the specification; asks questions concerning unaccounted aspects of the task. Offers alternatives and follows the stated requirements based on personal experience.
Databases Knows the basics of databases and transactions; writes simple select requests. Designs normalised database diagrams with consideration of the requests; skillfully uses views, saved procedures, triggers and user defined data types; knows the difference between clustered and unclustered indices; is a specialist in ORM tools. Administers and optimises databases, increases their efficiency; writes complex select requests; substitutes cursor use with SQL function calls; understands the principles of backend storage of data and indices; understands the process for creating “mirrors”, database replication etc.

Evaluation of the applicant's level can be subjective within reason, but the candidates usually agree with the results of their testing. During the probation period of employment, the candidate will in most cases have the opportunity to quickly demonstrate an aptitude level higher than the evaluated one.