├── README.md ├── guides ├── README.md ├── front-end.md ├── git.md ├── javascript-style-guide.md └── scrum.md └── recruitment ├── README.md ├── advanced-html5 └── README.md ├── basic-back-end └── README.md ├── basic-html5 └── README.md └── html5 ├── README.md └── styleguide.png /README.md: -------------------------------------------------------------------------------- 1 | # Nord Software Docs 2 | 3 | - [Guides](guides) 4 | - [Recruitment](recruitment) 5 | -------------------------------------------------------------------------------- /guides/README.md: -------------------------------------------------------------------------------- 1 | # Guides 2 | 3 | - [Front-end Developer’s Guide](front-end.md) 4 | - [JavaScript Style Guide](javascript-style-guide.md) 5 | - [Git](git.md) 6 | - [Scrum in a Nutshell](scrum.md) 7 | -------------------------------------------------------------------------------- /guides/front-end.md: -------------------------------------------------------------------------------- 1 | # Front-end Developer’s Guide 2 | 3 | This document is a primer on core concepts and workflows adopted by front-end developers at Nord Software. 4 | 5 | ## What is a front-end application? 6 | 7 | A front-end application is a graphical user interface consumed by actual end users. Technically it is a standalone server-agnostic JavaScript, HTML and CSS bundle that functions independently and, if needed, communicates with back-end services via HTTP or [WebSockets](http://www.html5rocks.com/en/tutorials/websockets/basics/). 8 | 9 | Web applications have traditionally been monolithic server-side systems (commonly written in PHP, Ruby or Python) that implemented the entire application architecture as a single server-side model. This meant that database connections, data models, business logic, HTML, CSS, JavaScript as well as any third-party integrations had to be tightly coupled under a single umbrella. Furthermore, from an end user’s point of view, it only provided a single entry into the application: through a web browser. 10 | 11 | The emergence of mobile applications has brought forth a looser, compositional paradigm to web application architecture that separates front-end and back-end components. Creating a standalone mobile application on top of a traditional web application would require the development team to rewrite large portions of the application to support communication with external client applications because it was originally meant for a single client only: the server-side application itself. 12 | 13 | So instead, by decoupling the presentational layer (HTML, CSS and JavaScript) entirely, the back-end application can be refined for a specific purpose: managing business-related processing and handling requests sent by front-end applications. Correspondingly, multiple consumer-facing front-end applications can be developed side-by-side against a shared back-end configuration. 14 | 15 | As a result, consumer use cases (e.g. requesting the most popular blog posts or submitting a comment) can be abstracted out and exposed by back-end services as public API endpoints which can then be called by front-end applications. This allows developers to build an extensible ecosystem around a business. 16 | 17 | ## Tools of the trade 18 | 19 | - [Atom](https://atom.io), [PHPStorm](https://www.jetbrains.com/phpstorm) (optional), or [Sublime Text](http://www.sublimetext.com/2) 20 | - [SourceTree](https://www.sourcetreeapp.com) 21 | - [iTerm 2](https://www.iterm2.com) 22 | - Node.js (install with [nvm](https://github.com/creationix/nvm)) 23 | - [Yarn](https://yarnpkg.com) 24 | - [Postman](https://www.getpostman.com) 25 | - [Gmail](http://www.gmail.com) 26 | - [Slack](https://nordsoftware.slack.com) 27 | - [Toggl](https://www.toggl.com) 28 | - [GitHub](https://github.com) 29 | - [Trello](https://trello.com) 30 | - [1Password](https://agilebits.com/onepassword) 31 | 32 | ## Accounts and licenses 33 | 34 | Please ask Janne to create the following accounts for you: 35 | 36 | - Gmail 37 | - Slack 38 | - Toggl 39 | - 1Password (license) 40 | 41 | Optional: 42 | 43 | - PHPStorm (license) 44 | 45 | ## Readme 46 | 47 | Every project **must** contain a README.md file that succinctly details the purpose of the project and the exact steps a new contributor should take in order to make the application run. 48 | 49 | Ideally, installing an application should be as simple as running 50 | 51 | ``` 52 | git clone git@github.com:nordsoftware/their-project.git . 53 | cd their-project 54 | yarn install 55 | yarn start 56 | ``` 57 | 58 | Although these commands may seem self-explanatory to experienced front-end developers, having a clear set of step-by-step instructions reduces the time and cognitive load it takes for a new contributor to get up to speed. 59 | 60 | Any additional steps should be clearly outlined in the readme. Before sharing the project with other developers, the project creator should test and make sure the installation guide works. 61 | 62 | As a project evolves, every contributor is responsible for ensuring the readme reflects the current state of the application. 63 | 64 | Remember to keep the readme concise and readable. Include what is absolutely necessary and omit everything else. 65 | 66 | ## Creating a new project 67 | 68 | ``` 69 | mkdir my-project 70 | cd my-project 71 | yarn init 72 | yarn add react 73 | yarn add -D webpack 74 | ``` 75 | 76 | ## Project structure 77 | 78 | ``` 79 | . 80 | ├── assets 81 | │   ├── background.jpg 82 | │   └── logo.png 83 | ├── dist 84 | │   ├── bundle.css 85 | │   ├── bundle.js 86 | │   └── index.html 87 | ├── src 88 | | ├── common 89 | | │   ├── services 90 | | │   │   └── authService.js 91 | | │   └── styles 92 | | │   └── grid.css 93 | | ├── components 94 | | │   ├── login-page 95 | | │   │   ├── LoginScreen.css 96 | | │   │   └── LoginScreen.js 97 | | │   └── navbar 98 | | │   ├── Navbar.css 99 | | │   └── Navbar.js 100 | | ├── config 101 | | │   └── api.js 102 | | └── index.js 103 | ├── .editorconfig 104 | ├── .eslintrc 105 | ├── .gitignore 106 | ├── package.json 107 | ├── README.md 108 | └── yarn.lock 109 | ``` 110 | 111 | A front-end project should contain at least two key directories: `src` and `dist`, referring to *source* and *distribution*, respectively. The source directory is where we save the code we’ve written, whereas the distribution directory contains the final executable code after compiling and building the application from source. You should not add `dist` to version control. An additional `assets` directory may be created to house images, fonts, audio files, and other presentational resources. 112 | 113 | Files and directories in `src` should be written in camel case (e.g. `authService.js`). A JavaScript module that exports a class should be capitalized (e.g. `LoginScreen.js`). Also, a CSS module pertaining to a component should respect the same naming convention as the component class (e.g. `LoginScreen.css`). 114 | 115 | ## Bower vs npm vs Yarn 116 | 117 | Bower was formerly used by front-end projects. 118 | 119 | npm is used by Node and Webpack-based projects. 120 | 121 | Generally speaking, Bower is used by older JavaScript applications where separate source files are simply concatenated to form a single bundle. Bower components utilize global variables to communicate with each other. Please use Bower only if you’re working on an existing project that already uses Bower. Note that Bower-based applications also typically contain a package.json file (npm’s manifest) for managing developer tool dependencies. 122 | 123 | Owing to the popularity of Webpack and Browserify, npm has become the go-to package manager for front-end applications as well. The universalization of npm modules now allows front-end developers to embrace the same conveniences that have made the Node ecosystem great. The encapsulated nature of modules brings greater clarity and improved structure to applications. 124 | 125 | [Yarn](https://yarnpkg.com/en/) improves on npm by providing an offline cache and a real lockfile (`yarn.lock`) for locking down exact package versions. 126 | 127 | If you're starting a new project, you should use Yarn. 128 | 129 | ## Typical development workflow 130 | 131 | ``` 132 | git pull --rebase 133 | yarn install 134 | yarn start 135 | git add . 136 | git commit -m "Add feature" 137 | git push origin master 138 | yarn deploy 139 | ``` 140 | 141 | ## Starting a development server 142 | 143 | Every project should include a *start* script that lifts up a development environment when a developer executes `yarn start` on the command-line. 144 | 145 | Scripts should only reference local npm modules that are included as dependencies (avoid referencing globally installed packages): 146 | 147 | package.json 148 | 149 | ```json 150 | { 151 | "scripts": { 152 | "start": "node node_modules/webpack-dev-server/bin/webpack-dev-server" 153 | }, 154 | "devDependencies": { 155 | "webpack-dev-server": "^1.14.1" 156 | } 157 | } 158 | ``` 159 | 160 | ## Dependencies 161 | 162 | Dependencies are stored in a `package.json` file (older projects may have a bower.json file for application dependencies). There are two types of dependencies: *application* and *development* dependencies. 163 | 164 | Application dependencies are essential packages that are necessary for an application to function. At build time, these dependencies are woven into the distributable bundles to be run on consumer devices. For example, a React-powered website needs the React module in order to render the pages properly, making it an application dependency. Such libraries should be installed with the `--save` flag. 165 | 166 | While development dependencies such as Webpack and Gulp may be required for an application to be correctly compiled, built and deployed, they do not affect application-level logic or implementations. Swapping them out should not alter the end product. Development dependencies are placed in `devDependencies` with the `--save-dev` option. 167 | 168 | ## Environments 169 | 170 | An environment is the context an application is running in. Typically, an application runs in at least two environments: a *development* environment (also referred to as a local environment), and a *production* environment. 171 | 172 | The development environment is an isolated instance of the application that runs on the developer’s own computer. This is where all the programming happens. The developer has the freedom to extensively test his implementations without having to worry about real-world repercussions. 173 | 174 | The production environment is the real, published state of the application running on a dedicated platform. This is the product that end users interact with. The production environment is a sacred domain that must be kept in a working condition at all times. 175 | 176 | Each environment shares the same front-end codebase but has its own set of back-end services like databases, email dispatchers, and third-party APIs. This is why we need a clean way to declare separate configurations for each environment. 177 | 178 | It should work like so: 179 | 180 | - Load the development configuration when lifting up the development environment (development build). 181 | - Load the production configuration when deploying to production (production build). 182 | 183 | Most configurations also contain various types of sensitive data such as database passwords and API keys. It’s important that not a single piece of sensitive information should ever end up in a repository. The decision to open-source a codebase may entail unintentionally publishing a set of credentials and compromising an entire service and the privacy of customers. 184 | 185 | Configuration data should be stored in environment variables (or in unversioned local configuration files) that can be accessed by build tools (e.g. Webpack or Gulp) to assemble the application. The build tool will then pull out the correct variables depending on whether you’re creating a local or production build. Front-end applications typically use a `.env` file for declaring environment variables. 186 | 187 | ## Version control 188 | 189 | Version control (or source control) brings organizational sanity to programmers. First and foremost, version control systems like Git establish a chronological timeline for your codebase, describing what was modified, added or removed by whom and for what reason. 190 | 191 | Issuing a commit is like taking a snapshot of the changes you’ve made. You handpick the changes you wish to include in your commit, write a concise message (e.g. “Decrease navbar font size”), and push it onto the timeline. Other collaborators may then pull your commit and receive your changes. 192 | 193 | Commits can be browsed and “time-traveled” to when needed. So instead of arbitrarily modifying a shared folder in Dropbox, you’ll be able to preserve and visualize the entire history and evolution of your codebase since its inception. 194 | 195 | Another therapeutic feature of version control systems is that they allow you to create branches. Branches are used for isolating portions of code and preventing them from causing side effects in other parts of the codebase while still in development. 196 | While projects may employ various branching models, the general idea is to have a stable branch called a *master* branch. This is the main branch that contains the most up-to-date stable version of the project. All unfinished bits of code must be kept out of the master branch, and every contributor should strive to keep it in a working (i.e. unbroken) state. Unfinished code should reside in a separate topic branch. 197 | 198 | I advise you to keep your commits compact. Bugs are harder to isolate and debug when time-traveling between massive commits. Smaller and more focused commit messages are also easier to scan visually. A lengthier and more descriptive timeline usually beats a shorter and nebulous one. 199 | 200 | ## Code readability and commenting 201 | 202 | Ideally, the best kind of code documentation is self-documenting code. In practice, it means that your code reads naturally. 203 | 204 | When writing a docblock comment for a function, ask yourself whether the function really needs a description, or any other written documentation for that matter. Could the function be refactored (restructured and reorganized) to make such comments obsolete? 205 | 206 | Consider the following function variations: 207 | 208 | ```javascript 209 | /** 210 | * Fetches blog posts by a user. 211 | */ 212 | const blogPosts = (id: string): Promise> => 213 | // Fetches blog posts from the API where the user ID is ‘id’ 214 | service.posts(id) 215 | 216 | const fetchBlogPostsByUserId = (userId: string): Promise> => 217 | blogPostApi.findAll({userId}) 218 | ``` 219 | 220 | Both functions are supposed to fetch blog posts from the API with the given user ID. The only difference is how the functions have been laid out. 221 | 222 | First, let’s look at the function `blogPosts`. There I’ve explicitly described the behavior of the function lest other developers misinterpret it. It takes `id` as an argument, but we’re not quite sure if it’s a blog post ID or a user ID. The description vaguely suggests the function accepts a user IDs since we’re fetching blog posts by a user, but it’s not 100% clear until we dive in read the comment inside the function. This problem becomes more apparent when reading calls to the function: `blogPosts(someId).then()...` because *users* may not be mentioned at all, forcing you to dig up the function declaration and read the comments. Also, the function’s internal operation is equally ambiguous since it refers to a `posts` method declared on an arbitrary `service` object which, again, takes a mysterious ID as an argument. 223 | 224 | Now, if we inspect the second, refactored function, you’ll notice that we’ve renamed the function and omitted the description. But we’ve added param and return sections to the docblock. They’ve been added so contributors can reference what data types are given to and returned by the function. I’ve also renamed the internals to clarify how the operation works as a whole. 225 | 226 | Just don’t go overboard with detail: `fetchBlogPostsByUserIdAndReturnPromise` may also hinder readability since most developers already know that making an HTTP fetch request is asynchronous in nature and should always return a promise object. Let’s keep our names intuitive and also pleasing to look at. 227 | 228 | ## Deployment 229 | 230 | Deployment is the process of publishing an application in a given environment (typically production). 231 | 232 | Deployment processes vary depending on the type of application, its dependencies, configuration, hosting services, third-party integrations, and any other characteristics that contribute to its complexity. There really is no one-size-fits-all method. Ideally, deploying an application (especially updating it) should not be made more complicated than executing a single terminal command. 233 | 234 | The reason why simplicity matters is that it encourages developers to deploy more often. Frequent deploys means releasing bite-sized features on a more regular basis. While deploying more often does result in introducing bugs more frequently, it reduces the number of large-scale bugs and regressions. From a developer’s point of view, it’s more desirable to fix minor bugs here and there than tackle large ones that have broken the entire application. Much like focused Git commits, smaller releases will be easier to debug as well. It’s also worth noting that clients and stakeholders will appreciate being able to see new updates more regularly. 235 | 236 | TL;DR: Deploy often. Deploy small. Fix more often but on a smaller scale. 237 | 238 | ## Hosting a front-end application 239 | 240 | A front-end application should be a server-agnostic bundle that can be served by any HTTP server or service. 241 | 242 | Hosting is a decision that should take the rest of the application architecture into consideration. Most of our front-end applications are served statically over a CDN like Amazon S3. 243 | 244 | ## Great resources 245 | 246 | - [react-boilerplate](https://github.com/nordsoftware/react-boilerplate) A template for jump-starting a React project 247 | - [Egghead.io](https://egghead.io) Plenty of bite-sized React and Redux tutorials 248 | - [JavaScript Weekly](http://javascriptweekly.com) A weekly email round-up of JavaScript news and articles 249 | - [React Newsletter](http://reactjsnewsletter.com) A weekly newsletter about React 250 | - [JavaScript Jabber](https://devchat.tv/js-jabber) A podcast about all things JavaScript 251 | -------------------------------------------------------------------------------- /guides/git.md: -------------------------------------------------------------------------------- 1 | Git 2 | === 3 | 4 | In order to keep the master branch clean and stable, new development code should be committed to separate feature 5 | branches. 6 | 7 | For example, let's say you're going to start writing a RESTful endpoint for creating new polls. You would start 8 | out by checking out the master branch with `git checkout master`, pulling any new changes with `git pull`, and 9 | creating a new feature branch (`git branch create-poll-endpoint`). 10 | 11 | Start coding and commit your changes at regular intervals to avoid pushing massive chunks. I'd also recommend 12 | pushing to origin (GitHub) after each commit (`git push origin create-poll-endpoint`) so that others can view 13 | your changes as early as possible, and to avoid losing your work when your hard drive blows up. 14 | 15 | When you're done building the feature, make sure to push all your changes to origin, and create a new pull 16 | request by opening up the GitHub repository in your browser and clicking **New pull request**. Make sure the 17 | **base** is set to `master` and **compare** points to your feature branch (e.g. `create-poll-endpoint`). After 18 | proofreading your changes, hit **Create pull request**. 19 | 20 | Now you may notify other developers to let them know you've completed a feature and it's ready to be reviewed. 21 | A pull request will then be either improved and polished upon the reviewer's request or merged into master. 22 | -------------------------------------------------------------------------------- /guides/javascript-style-guide.md: -------------------------------------------------------------------------------- 1 | # Nord Software JavaScript Style Guide 2 | 3 | ## Table of Contents 4 | 5 | 1. [General](#general) 6 | 1. [Strings](#strings) 7 | 1. [Functions](#functions) 8 | 1. [Arrays](#arrays) 9 | 1. [Type Coercion](#type-coercion) 10 | 1. [Comments](#comments) 11 | 1. [Modules](#modules) 12 | 13 | ## General 14 | 15 | - **Set tabs to 2 spaces** 16 | 17 | ```javascript 18 | // Bad 19 | 20 | if (isAdmin) { 21 | handleAdmin(); 22 | } 23 | 24 | // Good 25 | 26 | if (isAdmin) { 27 | handleAdmin(); 28 | } 29 | ``` 30 | 31 | - **Use semicolons** 32 | 33 | ```javascript 34 | // Bad 35 | 36 | const username = 'tiff' 37 | return username 38 | 39 | // Good 40 | 41 | const username = 'tiff'; 42 | return username; 43 | ``` 44 | 45 | - **Use triple equals** 46 | 47 | ```javascript 48 | // Bad 49 | 50 | if (name == 'Tiffany') {} 51 | if (age == 21) {} 52 | if (name != 'Caleb') {} 53 | 54 | // Good 55 | 56 | if (name === 'Tiffany') {} 57 | if (age === 21) {} 58 | if (name !== 'Caleb') {} 59 | ``` 60 | 61 | - **Space before leading brace** 62 | 63 | ```javascript 64 | // Bad 65 | 66 | function foo(){ 67 | } 68 | 69 | // Good 70 | 71 | function foo() { 72 | } 73 | ``` 74 | 75 | - **Space after `switch` keyword** 76 | 77 | ```javascript 78 | // Bad 79 | 80 | switch(action.type) {} 81 | 82 | // Good 83 | 84 | switch (action.type) {} 85 | ``` 86 | 87 | - **Blank line between switch cases** 88 | 89 | ```javascript 90 | // Bad 91 | 92 | case ADMIN: 93 | return 'You are an admin.'; 94 | case MODERATOR: 95 | return 'You are a moderator.'; 96 | 97 | // Good 98 | 99 | case ADMIN: 100 | return 'You are an admin.'; 101 | 102 | case MODERATOR: 103 | return 'You are a moderator.'; 104 | ``` 105 | 106 | - **Space after `if` keyword** 107 | 108 | ```javascript 109 | // Bad 110 | 111 | if(isAuthenticated) {} 112 | 113 | // Good 114 | 115 | if (isAuthenticated) {} 116 | ``` 117 | 118 | - **Simply return instead of using `else if` and `else` conditions** 119 | 120 | ```javascript 121 | // Bad 122 | 123 | if (isAdmin) { 124 | return 'You are an admin.'; 125 | } else if (isModerator) { 126 | return 'You are a moderator.' 127 | } else { 128 | return 'You are a user.'; 129 | } 130 | 131 | // Good 132 | 133 | if (isAdmin) { 134 | return 'You are an admin.'; 135 | } 136 | 137 | if (isModerator) { 138 | return 'You are a moderator.' 139 | } 140 | 141 | return 'You are a user.'; 142 | ``` 143 | 144 | - **Always use braces with `if` blocks** 145 | 146 | ```javascript 147 | // Bad 148 | 149 | if (isAdmin) 150 | return handleAdmin(); 151 | 152 | // Good 153 | 154 | if (isAdmin) { 155 | return handleAdmin(); 156 | } 157 | ``` 158 | 159 | - **Place `else if` and `else` on the same line as the previous closing brace** 160 | 161 | ```javascript 162 | // Bad 163 | 164 | if (isAdmin) { 165 | func1(); 166 | } 167 | else if (isModerator) { 168 | func2(); 169 | } 170 | else { 171 | func3(); 172 | } 173 | 174 | // Good 175 | 176 | if (isAdmin) { 177 | func1(); 178 | } else if (isModerator) { 179 | func2(); 180 | } else { 181 | func3(); 182 | } 183 | ``` 184 | 185 | - **Use `const` instead of `var` and `let`** 186 | 187 | ```javascript 188 | // Bad 189 | 190 | var accessToken = getAccessToken(); 191 | let user = getUser(accessToken); 192 | 193 | // Good 194 | 195 | const accessToken = getAccessToken(); 196 | const user = getUser(accessToken); 197 | ``` 198 | 199 | - **No padding inside functions** 200 | 201 | ```javascript 202 | // Bad 203 | 204 | function foo() { 205 | 206 | return true; 207 | 208 | } 209 | 210 | // Good 211 | 212 | function foo() { 213 | return true; 214 | } 215 | ``` 216 | 217 | - **No padding inside objects** 218 | 219 | ```javascript 220 | // Bad 221 | 222 | const foo = { 223 | 224 | bar: 'bar', 225 | baz: 'baz', 226 | quux() { 227 | return 'quux'; 228 | } 229 | 230 | }; 231 | 232 | // Good 233 | 234 | const foo = { 235 | bar: 'bar', 236 | baz: 'baz', 237 | quux() { 238 | return 'quux'; 239 | } 240 | }; 241 | ``` 242 | 243 | ## Strings 244 | 245 | - **Use single quotes** 246 | 247 | ```javascript 248 | // Bad 249 | 250 | const foo = "bar"; 251 | 252 | // Good 253 | 254 | const foo = 'bar'; 255 | ``` 256 | 257 | - **Use template strings for interpolation** 258 | 259 | ```javascript 260 | // Bad 261 | 262 | const name = 'Tiffany'; 263 | return 'Hi, I\'m ' + name + '.'; 264 | 265 | // Good 266 | 267 | const name = 'Tiffany'; 268 | return `Hi, I'm ${name}.`; 269 | ``` 270 | 271 | ## Functions 272 | 273 | - **Use function declarations** 274 | 275 | ```javascript 276 | // Bad 277 | 278 | const func = function() {}; 279 | 280 | // Good 281 | 282 | function func() {} 283 | ``` 284 | 285 | - **Use default parameters** 286 | 287 | ```javascript 288 | // Bad 289 | 290 | function log(message, level) { 291 | level = level || 'info'; 292 | } 293 | 294 | // Good 295 | 296 | function log(message, level = 'info') { 297 | } 298 | ``` 299 | 300 | - **Use arrow functions for callbacks** 301 | 302 | ```javascript 303 | // Bad 304 | 305 | fetch('http://api.example.com/posts').then(function(response) { 306 | return response.json(); 307 | }); 308 | 309 | // Good 310 | 311 | fetch('http://api.example.com/posts').then(response => response.json()); 312 | ``` 313 | 314 | - **Use the object method shorthand** 315 | 316 | ```javascript 317 | // Bad 318 | 319 | const foo = { 320 | bar: function() { 321 | console.log('You called foo.bar.'); 322 | } 323 | }; 324 | 325 | // Good 326 | 327 | const foo = { 328 | bar() { 329 | console.log('You called foo.bar.'); 330 | } 331 | }; 332 | ``` 333 | 334 | ## Arrays 335 | 336 | - **Use trailing commas** 337 | 338 | ```javascript 339 | // Bad 340 | 341 | const names = [ 342 | 'Tiffany' 343 | , 'Caleb' 344 | , 'Mark' 345 | ]; 346 | 347 | // Good 348 | 349 | const names = [ 350 | 'Tiffany', 351 | 'Caleb', 352 | 'Mark' 353 | ]; 354 | ``` 355 | 356 | - **Use Array.prototype.map instead of Array.prototype.forEach** 357 | 358 | ```javascript 359 | const oldArray = [ 360 | {name: 'Foo'}, 361 | {name: 'Bar'}, 362 | {name: 'Baz'} 363 | ]; 364 | 365 | // Bad 366 | 367 | const newArray = []; 368 | 369 | oldArray.forEach(item => { 370 | item.isVisible = true; 371 | 372 | newArray.push(item); 373 | }); 374 | 375 | // Good 376 | 377 | const newArray = oldArray.map(item => Object.assign({}, { 378 | isVisible: true 379 | }, item)); 380 | ``` 381 | 382 | - **Use Array.prototype.filter instead of Array.prototype.forEach** 383 | 384 | ```javascript 385 | const oldArray = [ 386 | {name: 'Foo'}, 387 | {name: 'Bar', isVisible: true}, 388 | {name: 'Baz'} 389 | ]; 390 | 391 | // Bad 392 | 393 | const newArray = []; 394 | 395 | oldArray.forEach(item => { 396 | if (item.isVisible) { 397 | newArray.push(item); 398 | } 399 | }); 400 | 401 | // Good 402 | 403 | const newArray = oldArray.filter(item => item.isVisible); 404 | ``` 405 | 406 | - **Use Array.prototype.reduce instead of Array.prototype.forEach** 407 | 408 | ```javascript 409 | const array = [ 410 | {id: 1, name: 'Foo'}, 411 | {id: 2, name: 'Bar'}, 412 | {id: 3, name: 'Baz'} 413 | ]; 414 | 415 | // Bad 416 | 417 | const map = {}; 418 | 419 | array.forEach(item => { 420 | map[item.id] = item; 421 | }); 422 | 423 | // Good 424 | 425 | const map = array.reduce((accumulator, item) => Object.assign({}, { 426 | [item.id]: item 427 | }, accumulator), {}); 428 | ``` 429 | 430 | ## Type Coercion 431 | 432 | - **Use `parseInt` with a radix for numbers** 433 | 434 | ```javascript 435 | const input = '21'; 436 | 437 | // Bad 438 | 439 | const age = new Number(input); 440 | 441 | 442 | // Good 443 | 444 | const age = parseInt(input, 10); 445 | ``` 446 | 447 | - **Use `parseFloat` for floating point numbers** 448 | 449 | ```javascript 450 | const input = '99.5'; 451 | 452 | // Bad 453 | 454 | const percentage = new Number(input); 455 | 456 | 457 | // Good 458 | 459 | const percentage = parseFloat(input); 460 | ``` 461 | 462 | - **Use `!!` for booleans** 463 | 464 | ```javascript 465 | const age = 0; 466 | 467 | // Bad 468 | 469 | const hasAge = new Boolean(age); 470 | 471 | // Good 472 | 473 | const hasAge = !!age; 474 | ``` 475 | 476 | - **Use `String` for strings** 477 | 478 | ```javascript 479 | const age = 21; 480 | 481 | // Bad 482 | 483 | const text = age + ''; 484 | 485 | // Good 486 | 487 | const text = String(age); 488 | ``` 489 | 490 | ## Comments 491 | 492 | - **Doc blocks** 493 | 494 | ```javascript 495 | /** 496 | * Logs an application message. 497 | * 498 | * @param {string} message 499 | * @param {string} [level] 500 | * 501 | * @return {string} 502 | */ 503 | function log(message, level = 'info') { 504 | } 505 | ``` 506 | 507 | ## Modules 508 | 509 | - **Separate third-party imports** 510 | 511 | ```javascript 512 | // Bad 513 | 514 | import React, {Component, PropTypes} from 'react'; 515 | import Navbar from './navbar'; 516 | import classnames from 'classnames'; 517 | import Button from '../common/button'; 518 | 519 | // Good 520 | 521 | import React, {Component, PropTypes} from 'react'; 522 | import classnames from 'classnames'; 523 | 524 | import Navbar from './navbar'; 525 | import Button from '../common/button'; 526 | ``` 527 | 528 | - **Reference named imports** 529 | 530 | ```javascript 531 | // Bad 532 | 533 | import React from 'react'; 534 | 535 | class Navbar extends React.Component { 536 | static propTypes = { 537 | items: React.PropTypes.array.isRequired 538 | }; 539 | } 540 | 541 | // Good 542 | 543 | import React, {Component, PropTypes} from 'react'; 544 | 545 | class Navbar extends Component { 546 | static propTypes = { 547 | items: PropTypes.array.isRequired 548 | }; 549 | } 550 | ``` 551 | -------------------------------------------------------------------------------- /guides/scrum.md: -------------------------------------------------------------------------------- 1 | # Scrum 2 | 3 | ## Life Cycle 4 | 5 | 1. Create an initial Product Backlog 6 | 2. Sprint Planning Meeting 7 | 3. Start Sprint (duration: 2 weeks) 8 | 4. Daily Standup Meeting 9 | 5. Backlog Refinement Meeting (mid-sprint) 10 | 6. Sprint Review Meeting (end of sprint) 11 | 7. Sprint Retrospective Meeting (end of sprint) 12 | 13 | Rinse and repeat. 14 | 15 | ## ScrumMaster 16 | 17 | A team member who facilitates Scrum (not a project manager). 18 | 19 | ## Product Owner 20 | 21 | A key stakeholder who assumes ownership of the product. 22 | 23 | ## Sprint 24 | 25 | A two-week period during which the team tries to achieve the sprint goal by completing the sprint backlog items. 26 | 27 | The outcome of a sprint is a product increment, which is a set of newly developed features. Ideally, the project should be in a state of being potentially shippable at the end of each sprint. 28 | 29 | ## User Story 30 | 31 | A concise description of a feature told from the perspective of the person who desires the new capability, commonly a user or customer of the system. 32 | 33 | - Example: *As an anonymous user, I want to be able to sign into the application.* 34 | 35 | ## Task 36 | 37 | An auxiliary item that facilitates the completion of a user story item. 38 | 39 | - Example: *Write an API controller that handles login requests.* 40 | 41 | ## Product Backlog 42 | 43 | A list of user story items that have not been implemented. 44 | 45 | ## Sprint Backlog 46 | 47 | A list of clearly defined and estimated user story items and tasks that will be implemented in the current sprint. 48 | 49 | ## Sprint Goal 50 | 51 | A one-/two-sentence description of what the team will strive to accomplish in a sprint. It is written collaboratively by the team and product owner. 52 | 53 | - Example: *Implement login functionality.* 54 | 55 | ## Sprint Planning Meeting 56 | 57 | The product owner describes the highest priority features to the team. The team asks any questions so that they can write detailed tasks based around high-level user stories. 58 | 59 | Sprint backlog items are estimated and pulled by developers (items should not be assigned to others). The team decides how much work they can accomplish in the coming sprint. 60 | 61 | - Goal: **Establish a sprint goal and a sprint backlog.** 62 | - Point in time: **Before starting a sprint** 63 | - Duration: **2 hours** 64 | - Attendants: **Product Owner, Scrum team, ScrumMaster** 65 | 66 | ## Daily Standup Meeting 67 | 68 | Each person attending a daily meeting answers the following questions: 69 | 70 | - What did you do last? 71 | - What will you do next? 72 | - Do you have any obstacles? 73 | 74 | Problems are not solved during the meeting but they are taken offline and usually dealt with by relevant team members after the meeting. 75 | 76 | - Goal: **Quick team-wide update, introduce problems** 77 | - Point in time: **Every day** 78 | - Duration: **15 minutes** 79 | - Attendants: **Scrum team, ScrumMaster, Product Owner** 80 | 81 | ## Backlog Refinement Meeting 82 | 83 | Product backlog item definitions are collaboratively improved. They are also estimated by the team. The product owner prioritizes user stories. 84 | 85 | - Goal: **Improve the product backlog for the next sprint planning meeting** 86 | - Point in time: **Mid-sprint** 87 | - Duration: **2 hours** 88 | - Attendants: **Scrum team, Product Owner, ScrumMaster** 89 | 90 | ## Sprint Review Meeting 91 | 92 | The team demos the project to the product owner and any stakeholders that may be attending the meeting. 93 | 94 | The team checks which sprint backlog items are considered done and assesses the project against the sprint goal. Incomplete items are returned to the product backlog and reprioritized by the product owner. 95 | 96 | The team creates new product backlog items based on the product owner’s and stakeholders’ feedback. 97 | 98 | - Goal: **Demo project, assess against sprint goal, create new items, reprioritize items** 99 | - Point in time: **End of sprint** 100 | - Duration: **2 hours** 101 | - Attendants: **Product Owner, Scrum team, stakeholders, ScrumMaster** 102 | 103 | ## Sprint Retrospective Meeting 104 | 105 | Each team member identifies specific things that the team should (1) start doing, (2) stop doing, and (3) continue doing. The team votes on items to focus on during the next sprint. The ScrumMaster should record these things in a shared document. 106 | 107 | The following sprint retrospective meeting is begun by reviewing the previous document and by assessing the team’s workflow. 108 | 109 | - Goal: **Evaluate a sprint’s workflow** 110 | - Point in time: **End of sprint** 111 | - Duration: **1 hour** 112 | - Attendants: **Scrum team, ScrumMaster, Product Owner** 113 | 114 | ## Story Points 115 | 116 | - *0* - does not take any time at all 117 | - *0.5* - a task that is smaller than the smallest task previously estimated 118 | - *Fibonacci numbers between 1 and 20* - a task that takes less than two days 119 | 120 | Any user stories larger than 20 story points should be broken down into smaller parts. 121 | -------------------------------------------------------------------------------- /recruitment/README.md: -------------------------------------------------------------------------------- 1 | # Tests 2 | 3 | ## Front-end 4 | 5 | - [HTML5 test](html5) 6 | 7 | ## Back-end 8 | 9 | - [Basic back-end test](basic-back-end) 10 | -------------------------------------------------------------------------------- /recruitment/advanced-html5/README.md: -------------------------------------------------------------------------------- 1 | # Advanced HTML5 test 2 | 3 | This test has been replaced with the [HTML5 test](../html5). -------------------------------------------------------------------------------- /recruitment/basic-back-end/README.md: -------------------------------------------------------------------------------- 1 | # Basic back-end test 2 | 3 | This is a preliminary test to determine the technical ability of developers applying for back-end positions at Nord Software. 4 | 5 | Before starting, please read carefully through the instructions below. You are free to look up any information online and offline and spend as much time on the test as you deem necessary. Please direct any questions to Eric Nishio (eric.nishio@nordsoftware.com). 6 | 7 | Your task is to create a small application that meets the following requirements: 8 | 9 | - Create a MySQL table called `person` that contains the following columns: **id** (auto-increment), **name** (varchar), **gender** (enum), **age** (int) 10 | - Write a PHP script that connects to the database and outputs all entries as JSON. 11 | - Write a PHP script that inserts a new person into the table based on POST data and outputs all entries (incl. the new person) as JSON. 12 | - Write a PHP script that deletes an existing person from the table based on their `id` and outputs all the remaining entries as JSON. 13 | 14 | You will receive extra points for 15 | 16 | - Input validation 17 | - Using object-oriented PHP 18 | - Using a PHP framework or library 19 | - RESTful API design 20 | - A developer-friendly installation guide 21 | 22 | Upon completing the test, please deliver the code as a zip file named `basic-back-end.zip` and send it to eric.nishio@nordsoftware.com. 23 | 24 | Good luck! 25 | -------------------------------------------------------------------------------- /recruitment/basic-html5/README.md: -------------------------------------------------------------------------------- 1 | # Basic HTML5 test 2 | 3 | This test has been replaced with the [HTML5 test](../html5). -------------------------------------------------------------------------------- /recruitment/html5/README.md: -------------------------------------------------------------------------------- 1 | # HTML5 test 2 | 3 | This is a preliminary test to determine the technical ability of developers applying for frontend positions at Digia. 4 | 5 | Before starting, please read carefully through the instructions below. You are free to look up any information online and offline and spend as much time on the test as you deem necessary. Please direct any questions to Eric Nishio at eric.nishio@digia.com. 6 | 7 | Your objective is to create a small signup form and a list of participants with Next.js and TypeScript, meeting the following requirements: 8 | 9 | 1. Use [Next.js](https://nextjs.org) to scaffold your application using TypeScript 10 | 2. Generate 20 participants that contain imaginary values for the following properties: *id*, *name*, *email address*, and *phone number* 11 | 3. Render a table that displays the participants on individual rows 12 | 4. Create a form for adding new participants to the table (remember to validate the form) 13 | 5. Make each participant editable by clicking on a table cell (inline editing) 14 | 6. Add support for deleting rows 15 | 7. Make each column sortable upon clicking on a column header 16 | 8. Write a developer-friendly installation guide on how to run the app 17 | 9. Push your code to a public [GitHub](https://github.com) repository 18 | 10. Deploy a live build on the internet 19 | 11. **[FOLLOW THE DESIGN](styleguide.png) AS ACCURATELY AS YOU CAN** (as for the logo, feel free to render a blank square) 20 | 21 | Upon completing the test, please push your code to a new public repository on [GitHub](https://github.com), deploy a live build on the internet, and email both links to eric.nishio@digia.com. **Please refrain from including any file attachments as they will cause your email to end up in the spam folder.** 22 | 23 | Other things to note: 24 | 25 | - You do not need to write tests. 26 | - You do not need to use global state management. 27 | - You are free to write your CSS however you want. 28 | 29 | Good luck! 30 | -------------------------------------------------------------------------------- /recruitment/html5/styleguide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/digiaonline/docs/befa11f1c83b29422a9f0c0b78a953a09b14a4b5/recruitment/html5/styleguide.png --------------------------------------------------------------------------------