├── README.md
├── recruitment
├── html5
│ ├── styleguide.png
│ └── README.md
├── basic-html5
│ └── README.md
├── advanced-html5
│ └── README.md
├── README.md
└── basic-back-end
│ └── README.md
└── guides
├── README.md
├── git.md
├── scrum.md
├── javascript-style-guide.md
└── front-end.md
/README.md:
--------------------------------------------------------------------------------
1 | # Nord Software Docs
2 |
3 | - [Guides](guides)
4 | - [Recruitment](recruitment)
5 |
--------------------------------------------------------------------------------
/recruitment/html5/styleguide.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/digiaonline/docs/HEAD/recruitment/html5/styleguide.png
--------------------------------------------------------------------------------
/recruitment/basic-html5/README.md:
--------------------------------------------------------------------------------
1 | # Basic HTML5 test
2 |
3 | This test has been replaced with the [HTML5 test](../html5).
--------------------------------------------------------------------------------
/recruitment/advanced-html5/README.md:
--------------------------------------------------------------------------------
1 | # Advanced HTML5 test
2 |
3 | This test has been replaced with the [HTML5 test](../html5).
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------