SlideShare uma empresa Scribd logo
1 de 68
Baixar para ler offline
Ho imparato qualcosa di Elixir
Il Test-Driven Development e il Clean Code come supporto per apprendere
un nuovo linguaggio di programmazione
CODEMOTION MILAN - SPECIAL EDITION
10 – 11 NOVEMBER 2017
Ho imparato qualcosa di Elixir
Il Test-Driven Development e il Clean Code come supporto per apprendere un
nuovo linguaggio di programmazione
Warming up
Developers
Development
Industry
https://www.pexels.com/photo/amphitheater-ancient-arch-architecture-397431/
Developers
Scientists
Engineers
Self taught
https://static.pexels.com/photos/259335/pexels-photo-259335.jpeg
We are good at writing code!
https://static.pexels.com/photos/225769/pexels-photo-225769.jpeg
Software Development
“Costruiamo” software allo stesso modo di come si costruiscono ponti
https://www.pexels.com/photo/architecture-area-bay-bridge-356968/
tutti i sistemi costruiti
con lo stesso processo di sviluppo
condividono la stessa caratteristica
We did it and it works!
https://www.pexels.com/photo/architecture-building-clouds-contemporary-388419/
resistance to change
Faremo manutenzione …
(prima o poi)
● Fix a bug
● Add new features
● Other Changes:
Because changes happens
https://static.pexels.com/photos/1599/building-vehicle-motorbike-motorcycle.jpg
Potremo percepire
qualcosa di strano
https://static.pexels.com/photos/560/feet-legs-animal-farm.jpg
Come è potuto accadere?
Abitudini
Il processo di sviluppo
Pratiche e disciplina
I want to tell you a little secret
I am
an impostor
https://www.pexels.com/photo/black-and-white-man-person-cigarette-543/
I am not the best
computer programmer
in the world
Discipline
Practices
Discipline
Practices
CHAPTER ONE:
il nostro primo test
alla ricerca della comfort zone
mix new hello_world
mix test
defmodule HelloWorldTest do
use ExUnit.Case, async: true
test "should return a greeting" do
assert "hello world" == HelloWorld.greet()
end
end
HelloWorldTest
* test should return a greeting (1.1ms)
Finished in 0.03 seconds
1 test, 1 failure
Randomized with seed 689156
defmodule HelloWorld do
def greet() do
"hello world"
end
end
.
Finished in 0.02 seconds
1 test, 0 failures
Randomized with seed 877741
HelloWorldTest
* test should return a greeting (1.3ms)
Finished in 0.03 seconds
1 test, 0 failures
Randomized with seed 689156
CHAPTER TWO:
test the documentation
seguire la documentazione scrivendo prima i test
defmodule GreetingTest do
use ExUnit.Case, async: true
test "should return a greeting" do
pid = spawn(Greeting, :loop, [])
send pid, {self(), :greet}
assert_receive "hello world"
end
end
defmodule Greeting do
def loop do
receive do
{from_pid, :greet} ->
send from_pid, "hello world"
loop()
end
end
end
defmodule GreetingTest do
use ExUnit.Case, async: true
test "should return a greeting" do
pid = spawn(Greeting, :loop, [])
send pid, {self(), :greet}
assert_receive "hello world"
end
end
defmodule GreetingTest do
use ExUnit.Case, async: true
test "should return a greeting" do
pid = Greeting.start
response = Greeting.greet(pid)
assert "hello world" == response
end
end
defmodule Greeting do
def start do
spawn(fn -> loop() end)
end
def greet(pid) do
send pid, {self(), :greet}
receive do
reply -> reply
end
end
...
test "should return a greeting in spanish" do
pid = Greeting.start(:spanish)
response = Greeting.greet(pid)
assert "hola mundo" == response
end
defmodule Greeting do
def start(language  nil) do
spawn(fn -> loop(language) end)
end
defp loop(language) do
receive do
{from_pid, :greet} ->
send from_pid, greet_message_for(language)
loop(language)
end
end
defp greet_message_for(:spanish), do: "hola mundo"
defp greet_message_for(_), do: "hello world"
end
CHAPTER THREE:
the bank account
proviamo a scrivere una piccola applicazione
The Bank Account Application
TODO
- Posso creare un nuovo account
- Posso eliminare un account
- Posso controllare il saldo corrente
per ciascun account
- Posso depositare un importo
- Posso prelevare un importo
DOING
The Bank Account Application
TODO
- Posso creare un nuovo account
- Posso eliminare un account
- Posso controllare il saldo corrente
per ciascun account
- Posso depositare un importo
- Posso prelevare un importo
DOING
test "should create a new account" do
bank_pid = Bank.start()
created_response = Bank.create_account(bank_pid, "non_existing_account")
assert {:ok, :account_created} == created_response
end
defmodule Bank do
def start() do
nil
end
def create_account(_pid, _name) do
{:ok, :account_created}
end
end
test "should return an error when the account exists" do
bank_pid = Bank.start()
Bank.create_account(bank_pid, "non_existing_account")
response = Bank.create_account(bank_pid, "non_existing_account")
assert {:error, :account_already_exists} == response
end
defmodule Bank do
def start() do
spawn(fn -> loop(%{}) end)
end
def create_account(pid, name) do
send pid, {self(), {:create, name}}
receive do
reply -> reply
end
end
...
...
defp loop(accounts) do
receive do
{from_pid, {:create, name}} ->
{updated_accounts, message} = _create_account(name, accounts)
send from_pid, message
loop(updated_accounts)
end
end
defp _create_account(name, accounts) do
case Map.has_key?(accounts, account) do
true -> {accounts, {:error, :account_already_exists}}
_ -> {Map.put(accounts, name, nil), {:ok, :account_created}}
end
end
end
test "should delete an existing account" do
bank_pid = Bank.start()
not_exists_response = Bank.delete_account(bank_pid, "an_account")
Bank.create_account(bank_pid, "an_account")
deleted_response = Bank.delete_account(bank_pid, "an_account")
assert {:error, :account_not_exists} == not_exists_response
assert {:ok, :account_deleted} == deleted_response
end
defp loop(accounts) do
receive do
{from_pid, message} ->
{updated_accounts, reply} = handle_message(message, accounts)
send from_pid, reply
loop(updated_accounts)
end
end
defp handle_message({:create, name}, accounts)
case Map.has_key?(accounts, name) do
true -> {accounts, {:error, :account_already_exists}}
_ -> {Map.put(accounts, name, nil), {:ok, :account_created}}
end
end
defp handle_message({:delete, name}, accounts)
case Map.has_key?(accounts, name) do
true -> {Map.delete(accounts, name), {:ok, :account_deleted}}
_ -> {accounts, {:error, :account_not_exists}}
end
end
test "should return the current balance of an existing account" do
bank_pid = Bank.start()
Bank.create_account(bank_pid, "an_account")
response = Bank.check_balance(bank_pid, "an_account")
assert {:ok, 0} == response
end
def check_balance(pid, name) do
send pid, {self(), {:check_balance, name}}
receive do
reply -> reply
end
end
defp handle_message({:check_balance, name}, accounts)
case Map.has_key?(accounts, name) do
false -> {accounts, {:error, :account_not_exists}}
_ -> {accounts, {:ok, Map.get(accounts, name)}}
end
end
BankTest
* test when account exists we are able to deposit positive amounts (0.04ms)
* test when account exists we are not able to withdraw if the amount is greater than current balance (0.01ms)
* test when account exists we are able to delete an account (0.01ms)
* test when account does not exists we are able to create a new account (0.00ms)
* test when account exists we are not able to deposit negative amounts (0.01ms)
* test when account exists we are not able to withdraw a negative amount (0.01ms)
* test when account does not exists we are not able to check current balance (0.00ms)
* test when account does not exists we are not able to deposit (0.00ms)
* test when account exists we are able to withdraw if the amount is lower or equal than current balance (0.01ms)
* test when account does not exists we are not able to delete an account (0.00ms)
* test when account exists we are able to check the current balance (0.01ms)
* test when account exists we are not able to create an account with the same name (0.00ms)
* test when account exists we are able to perform actions on different accounts (0.02ms)
* test when account does not exists we are not able to withdraw (0.00ms)
Finished in 0.05 seconds
14 tests, 0 failures
Se provassimo a estrarre un processo per ogni account?
Questo è refactoring.
defp handle_message({:create, name}, accounts)
case Map.has_key?(accounts, name) do
true -> {accounts, {:error, :account_already_exists}}
_ ->
bank_account = BankAccount.start()
{Map.put(accounts, name, bank_account), {:ok, :account_created}}
end
end
E poi tante altre cose ...
Images from http://learnyousomeerlang.com
defmodule Bank do
use Application
def start(_type, _args) do
Bank.Supervisor.start_link([])
end
end
defmodule Bank.Supervisor do
use Supervisor
def start_link(opts) do
Supervisor.start_link(__MODULE__, :ok, opts)
end
def init(:ok) do
children = [
Bank,
AccountSupervisor
]
Supervisor.init(children, strategy: :one_for_one)
end
end
The Bank Account Application
TODO
- Posso creare un nuovo account
- Posso eliminare un account
- Posso controllare il saldo corrente
per ciascun account
- Posso depositare un importo
- Posso prelevare un importo
DOING
- Voglio esporre una API HTTP
The Bank Account Application
TODO
- Posso creare un nuovo account
- Posso eliminare un account
- Posso controllare il saldo corrente
per ciascun account
- Posso depositare un importo
- Posso prelevare un importo
DOING
- Voglio esporre una API HTTP
CHAPTER FOUR (the last one):
a RESTful API
esporre l’applicazione appena creata in RESTful
defmodule Http.HelloWorldTest do
use ExUnit.Case, async: true
use Plug.Test
@opts Http.HelloWorld.init([])
test "should return a greeting" do
conn =
conn(:get, "/greet")
|> Http.HelloWorld.call(@opts)
assert 200 == conn.status
assert "hello world" == conn.resp_body
end
end
defmodule Http.HelloWorld do
use Plug.Router
plug :match
plug :dispatch
get "/greet" do
send_resp(conn, 200, "hello world")
end
end
defmodule Http.RouterTest do
use ExUnit.Case, async: true
use Plug.Test
import Mock
@router Http.Router
@opts @router.init([])
test "should return 201 when a new account is created" do
with_mock Bank.Admin, [create_account: fn("joe") -> {:ok, :account_created} end] do
conn = do_request(:post, "/accounts/joe")
assert 201 == conn.status
assert "/accounts/joe" == conn.resp_body
end
end
defp do_request(verb, endpoint, payload) do
conn(verb, endpoint, payload)
|> @router.call(@opts)
end
end
defmodule Http.Router do
use Plug.Router
plug :match
plug :dispatch
post "/accounts/:account_name" do
{:ok, :account_created} = Bank.Admin.create_account(account_name)
send_resp(conn, 201, "/accounts/" <> account_name)
end
end
defmodule Http.RouterTest do
use ExUnit.Case, async: true
use Plug.Test
import Mox
@router Http.Router
@opts @router.init([])
test "returns 201 when a new account is created" do
expect(Bank.AdminMock, :create_account, fn("joe") -> {:ok, :account_created} end)
conn = do_request(:post, "/accounts/joe")
assert 201 == conn.status
assert "/accounts/joe" == conn.resp_body
verify! Bank.AdminMock
end
defp do_request(verb, endpoint, payload) do
conn(verb, endpoint, payload)
|> @router.call(@opts)
end
end
defmodule Http.Router do
use Plug.Router
plug :match
plug :dispatch
@bank_admin Application.get_env(:bank, :bank_admin)
post "/accounts/:account_name" do
{:ok, :account_created} = @bank_admin.create_account(account_name)
send_resp(conn, 201, "/accounts/" <> account_name)
end
end
defmodule Bank.Supervisor do
use Supervisor
def start_link(opts) do
Supervisor.start_link(__MODULE__, :ok, opts)
end
def init(:ok) do
children = [
Bank,
AccountSupervisor,
Plug.Adapters.Cowboy.child_spec(:http, Http.Router, [], [port: 4000])
]
Supervisor.init(children, strategy: :one_for_one)
end
end
# checkout the project
git clone https://github.com/joebew42/elixir_bank_account.git
cd elixir_bank_account/
mix test
iex -S mix
# create an account
curl -v -H "auth: joe" -X "POST" http://localhost:4000/accounts/joe
# deposit an amount
curl -v -H "auth: joe" -X "PUT" http://localhost:4000/accounts/joe/deposit/100
# check current balance
curl -v -H "auth: joe" -X "GET" http://localhost:4000/accounts/joe
{ "current_balance": 1100 }
# delete an account
curl -v -H "auth: joe" -X "DELETE" http://localhost:4000/accounts/joe
rhythm > process > learning > working software
Un attimo ...
Software Development
is a learning process
“Itis bettertobeawarriorinagarden
thantobeagardenerinawar”
the little warrior recipe:
look for continuous improvement
through deliberate practice
We have to be proud
about the way we work
and not of what we have
built
(it’s not about the tool)
twitch.tv/joebew42xpeppers.com/carriere
github.com/joebew42/study-path
HIRE
ME!
“Learn to build working software
by playing our preferred videogame …
… Writing code!” - @joebew42
This presentation is released under
Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0)
https://creativecommons.org/licenses/by-nc-sa/3.0/

Mais conteúdo relacionado

Mais procurados

Mais procurados (20)

EcmaScript unchained
EcmaScript unchainedEcmaScript unchained
EcmaScript unchained
 
Promise: async programming hero
Promise: async programming heroPromise: async programming hero
Promise: async programming hero
 
EcmaScript 6
EcmaScript 6 EcmaScript 6
EcmaScript 6
 
Swift internals
Swift internalsSwift internals
Swift internals
 
2020 Droid Knights CustomLint 적용기
2020 Droid Knights CustomLint 적용기2020 Droid Knights CustomLint 적용기
2020 Droid Knights CustomLint 적용기
 
ES6 PPT FOR 2016
ES6 PPT FOR 2016ES6 PPT FOR 2016
ES6 PPT FOR 2016
 
ECMAScript 6
ECMAScript 6ECMAScript 6
ECMAScript 6
 
ES6 - Next Generation Javascript
ES6 - Next Generation JavascriptES6 - Next Generation Javascript
ES6 - Next Generation Javascript
 
ES6 in Real Life
ES6 in Real LifeES6 in Real Life
ES6 in Real Life
 
java sockets
 java sockets java sockets
java sockets
 
Introduction into ES6 JavaScript.
Introduction into ES6 JavaScript.Introduction into ES6 JavaScript.
Introduction into ES6 JavaScript.
 
Hey Kotlin, How it works?
Hey Kotlin, How it works?Hey Kotlin, How it works?
Hey Kotlin, How it works?
 
LetSwift RxSwift 시작하기
LetSwift RxSwift 시작하기LetSwift RxSwift 시작하기
LetSwift RxSwift 시작하기
 
Explaining ES6: JavaScript History and What is to Come
Explaining ES6: JavaScript History and What is to ComeExplaining ES6: JavaScript History and What is to Come
Explaining ES6: JavaScript History and What is to Come
 
PHP Performance Trivia
PHP Performance TriviaPHP Performance Trivia
PHP Performance Trivia
 
Scalaz By Example (An IO Taster) -- PDXScala Meetup Jan 2014
Scalaz By Example (An IO Taster) -- PDXScala Meetup Jan 2014Scalaz By Example (An IO Taster) -- PDXScala Meetup Jan 2014
Scalaz By Example (An IO Taster) -- PDXScala Meetup Jan 2014
 
Typed Properties and more: What's coming in PHP 7.4?
Typed Properties and more: What's coming in PHP 7.4?Typed Properties and more: What's coming in PHP 7.4?
Typed Properties and more: What's coming in PHP 7.4?
 
C++ L08-Classes Part1
C++ L08-Classes Part1C++ L08-Classes Part1
C++ L08-Classes Part1
 
Nikita Popov "What’s new in PHP 8.0?"
Nikita Popov "What’s new in PHP 8.0?"Nikita Popov "What’s new in PHP 8.0?"
Nikita Popov "What’s new in PHP 8.0?"
 
A Gremlin ate my graph
A Gremlin ate my graphA Gremlin ate my graph
A Gremlin ate my graph
 

Semelhante a Joe Bew - Apprendi un nuovo linguaggio sfruttando il TDD e il Clean Code - Codemotion Milan 2017

Phoenix for laravel developers
Phoenix for laravel developersPhoenix for laravel developers
Phoenix for laravel developers
Luiz Messias
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Paulo Ragonha
 
Tdd for BT E2E test community
Tdd for BT E2E test communityTdd for BT E2E test community
Tdd for BT E2E test community
Kerry Buckley
 

Semelhante a Joe Bew - Apprendi un nuovo linguaggio sfruttando il TDD e il Clean Code - Codemotion Milan 2017 (20)

Tres Gemas De Ruby
Tres Gemas De RubyTres Gemas De Ruby
Tres Gemas De Ruby
 
Phoenix for laravel developers
Phoenix for laravel developersPhoenix for laravel developers
Phoenix for laravel developers
 
ES6, WTF?
ES6, WTF?ES6, WTF?
ES6, WTF?
 
Let it crash - fault tolerance in Elixir/OTP
Let it crash - fault tolerance in Elixir/OTPLet it crash - fault tolerance in Elixir/OTP
Let it crash - fault tolerance in Elixir/OTP
 
Ruby on Rails
Ruby on RailsRuby on Rails
Ruby on Rails
 
Rails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine
Rails-like JavaScript Using CoffeeScript, Backbone.js and JasmineRails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine
Rails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine
 
Migrating legacy data
Migrating legacy dataMigrating legacy data
Migrating legacy data
 
Very basic functional design patterns
Very basic functional design patternsVery basic functional design patterns
Very basic functional design patterns
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
 
Ruby on Rails testing with Rspec
Ruby on Rails testing with RspecRuby on Rails testing with Rspec
Ruby on Rails testing with Rspec
 
The biggest lies about react hooks
The biggest lies about react hooksThe biggest lies about react hooks
The biggest lies about react hooks
 
Why ruby
Why rubyWhy ruby
Why ruby
 
The Promised Land (in Angular)
The Promised Land (in Angular)The Promised Land (in Angular)
The Promised Land (in Angular)
 
SOLID Ruby, SOLID Rails
SOLID Ruby, SOLID RailsSOLID Ruby, SOLID Rails
SOLID Ruby, SOLID Rails
 
Chaining and function composition with lodash / underscore
Chaining and function composition with lodash / underscoreChaining and function composition with lodash / underscore
Chaining and function composition with lodash / underscore
 
Natural Task Scheduling Using Futures and Continuations, Ivan Čukić, Qt Devel...
Natural Task Scheduling Using Futures and Continuations, Ivan Čukić, Qt Devel...Natural Task Scheduling Using Futures and Continuations, Ivan Čukić, Qt Devel...
Natural Task Scheduling Using Futures and Continuations, Ivan Čukić, Qt Devel...
 
Damn Fine CoffeeScript
Damn Fine CoffeeScriptDamn Fine CoffeeScript
Damn Fine CoffeeScript
 
Tdd for BT E2E test community
Tdd for BT E2E test communityTdd for BT E2E test community
Tdd for BT E2E test community
 
Design Patterns the Ruby way - ConFoo 2015
Design Patterns the Ruby way - ConFoo 2015Design Patterns the Ruby way - ConFoo 2015
Design Patterns the Ruby way - ConFoo 2015
 
Acceptance Testing with Webrat
Acceptance Testing with WebratAcceptance Testing with Webrat
Acceptance Testing with Webrat
 

Mais de Codemotion

Mais de Codemotion (20)

Fuzz-testing: A hacker's approach to making your code more secure | Pascal Ze...
Fuzz-testing: A hacker's approach to making your code more secure | Pascal Ze...Fuzz-testing: A hacker's approach to making your code more secure | Pascal Ze...
Fuzz-testing: A hacker's approach to making your code more secure | Pascal Ze...
 
Pompili - From hero to_zero: The FatalNoise neverending story
Pompili - From hero to_zero: The FatalNoise neverending storyPompili - From hero to_zero: The FatalNoise neverending story
Pompili - From hero to_zero: The FatalNoise neverending story
 
Pastore - Commodore 65 - La storia
Pastore - Commodore 65 - La storiaPastore - Commodore 65 - La storia
Pastore - Commodore 65 - La storia
 
Pennisi - Essere Richard Altwasser
Pennisi - Essere Richard AltwasserPennisi - Essere Richard Altwasser
Pennisi - Essere Richard Altwasser
 
Michel Schudel - Let's build a blockchain... in 40 minutes! - Codemotion Amst...
Michel Schudel - Let's build a blockchain... in 40 minutes! - Codemotion Amst...Michel Schudel - Let's build a blockchain... in 40 minutes! - Codemotion Amst...
Michel Schudel - Let's build a blockchain... in 40 minutes! - Codemotion Amst...
 
Richard Süselbeck - Building your own ride share app - Codemotion Amsterdam 2019
Richard Süselbeck - Building your own ride share app - Codemotion Amsterdam 2019Richard Süselbeck - Building your own ride share app - Codemotion Amsterdam 2019
Richard Süselbeck - Building your own ride share app - Codemotion Amsterdam 2019
 
Eward Driehuis - What we learned from 20.000 attacks - Codemotion Amsterdam 2019
Eward Driehuis - What we learned from 20.000 attacks - Codemotion Amsterdam 2019Eward Driehuis - What we learned from 20.000 attacks - Codemotion Amsterdam 2019
Eward Driehuis - What we learned from 20.000 attacks - Codemotion Amsterdam 2019
 
Francesco Baldassarri - Deliver Data at Scale - Codemotion Amsterdam 2019 -
Francesco Baldassarri  - Deliver Data at Scale - Codemotion Amsterdam 2019 - Francesco Baldassarri  - Deliver Data at Scale - Codemotion Amsterdam 2019 -
Francesco Baldassarri - Deliver Data at Scale - Codemotion Amsterdam 2019 -
 
Martin Förtsch, Thomas Endres - Stereoscopic Style Transfer AI - Codemotion A...
Martin Förtsch, Thomas Endres - Stereoscopic Style Transfer AI - Codemotion A...Martin Förtsch, Thomas Endres - Stereoscopic Style Transfer AI - Codemotion A...
Martin Förtsch, Thomas Endres - Stereoscopic Style Transfer AI - Codemotion A...
 
Melanie Rieback, Klaus Kursawe - Blockchain Security: Melting the "Silver Bul...
Melanie Rieback, Klaus Kursawe - Blockchain Security: Melting the "Silver Bul...Melanie Rieback, Klaus Kursawe - Blockchain Security: Melting the "Silver Bul...
Melanie Rieback, Klaus Kursawe - Blockchain Security: Melting the "Silver Bul...
 
Angelo van der Sijpt - How well do you know your network stack? - Codemotion ...
Angelo van der Sijpt - How well do you know your network stack? - Codemotion ...Angelo van der Sijpt - How well do you know your network stack? - Codemotion ...
Angelo van der Sijpt - How well do you know your network stack? - Codemotion ...
 
Lars Wolff - Performance Testing for DevOps in the Cloud - Codemotion Amsterd...
Lars Wolff - Performance Testing for DevOps in the Cloud - Codemotion Amsterd...Lars Wolff - Performance Testing for DevOps in the Cloud - Codemotion Amsterd...
Lars Wolff - Performance Testing for DevOps in the Cloud - Codemotion Amsterd...
 
Sascha Wolter - Conversational AI Demystified - Codemotion Amsterdam 2019
Sascha Wolter - Conversational AI Demystified - Codemotion Amsterdam 2019Sascha Wolter - Conversational AI Demystified - Codemotion Amsterdam 2019
Sascha Wolter - Conversational AI Demystified - Codemotion Amsterdam 2019
 
Michele Tonutti - Scaling is caring - Codemotion Amsterdam 2019
Michele Tonutti - Scaling is caring - Codemotion Amsterdam 2019Michele Tonutti - Scaling is caring - Codemotion Amsterdam 2019
Michele Tonutti - Scaling is caring - Codemotion Amsterdam 2019
 
Pat Hermens - From 100 to 1,000+ deployments a day - Codemotion Amsterdam 2019
Pat Hermens - From 100 to 1,000+ deployments a day - Codemotion Amsterdam 2019Pat Hermens - From 100 to 1,000+ deployments a day - Codemotion Amsterdam 2019
Pat Hermens - From 100 to 1,000+ deployments a day - Codemotion Amsterdam 2019
 
James Birnie - Using Many Worlds of Compute Power with Quantum - Codemotion A...
James Birnie - Using Many Worlds of Compute Power with Quantum - Codemotion A...James Birnie - Using Many Worlds of Compute Power with Quantum - Codemotion A...
James Birnie - Using Many Worlds of Compute Power with Quantum - Codemotion A...
 
Don Goodman-Wilson - Chinese food, motor scooters, and open source developmen...
Don Goodman-Wilson - Chinese food, motor scooters, and open source developmen...Don Goodman-Wilson - Chinese food, motor scooters, and open source developmen...
Don Goodman-Wilson - Chinese food, motor scooters, and open source developmen...
 
Pieter Omvlee - The story behind Sketch - Codemotion Amsterdam 2019
Pieter Omvlee - The story behind Sketch - Codemotion Amsterdam 2019Pieter Omvlee - The story behind Sketch - Codemotion Amsterdam 2019
Pieter Omvlee - The story behind Sketch - Codemotion Amsterdam 2019
 
Dave Farley - Taking Back “Software Engineering” - Codemotion Amsterdam 2019
Dave Farley - Taking Back “Software Engineering” - Codemotion Amsterdam 2019Dave Farley - Taking Back “Software Engineering” - Codemotion Amsterdam 2019
Dave Farley - Taking Back “Software Engineering” - Codemotion Amsterdam 2019
 
Joshua Hoffman - Should the CTO be Coding? - Codemotion Amsterdam 2019
Joshua Hoffman - Should the CTO be Coding? - Codemotion Amsterdam 2019Joshua Hoffman - Should the CTO be Coding? - Codemotion Amsterdam 2019
Joshua Hoffman - Should the CTO be Coding? - Codemotion Amsterdam 2019
 

Último

Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 

Último (20)

Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdf
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 

Joe Bew - Apprendi un nuovo linguaggio sfruttando il TDD e il Clean Code - Codemotion Milan 2017

  • 1. Ho imparato qualcosa di Elixir Il Test-Driven Development e il Clean Code come supporto per apprendere un nuovo linguaggio di programmazione CODEMOTION MILAN - SPECIAL EDITION 10 – 11 NOVEMBER 2017
  • 2. Ho imparato qualcosa di Elixir Il Test-Driven Development e il Clean Code come supporto per apprendere un nuovo linguaggio di programmazione
  • 5. We are good at writing code! https://static.pexels.com/photos/225769/pexels-photo-225769.jpeg
  • 6. Software Development “Costruiamo” software allo stesso modo di come si costruiscono ponti https://www.pexels.com/photo/architecture-area-bay-bridge-356968/
  • 7. tutti i sistemi costruiti con lo stesso processo di sviluppo condividono la stessa caratteristica
  • 8. We did it and it works! https://www.pexels.com/photo/architecture-building-clouds-contemporary-388419/
  • 10. Faremo manutenzione … (prima o poi) ● Fix a bug ● Add new features ● Other Changes: Because changes happens https://static.pexels.com/photos/1599/building-vehicle-motorbike-motorcycle.jpg
  • 11. Potremo percepire qualcosa di strano https://static.pexels.com/photos/560/feet-legs-animal-farm.jpg
  • 12. Come è potuto accadere?
  • 13. Abitudini Il processo di sviluppo Pratiche e disciplina
  • 14. I want to tell you a little secret
  • 16. I am not the best computer programmer in the world
  • 18. CHAPTER ONE: il nostro primo test alla ricerca della comfort zone
  • 20. defmodule HelloWorldTest do use ExUnit.Case, async: true test "should return a greeting" do assert "hello world" == HelloWorld.greet() end end
  • 21. HelloWorldTest * test should return a greeting (1.1ms) Finished in 0.03 seconds 1 test, 1 failure Randomized with seed 689156
  • 22. defmodule HelloWorld do def greet() do "hello world" end end
  • 23. . Finished in 0.02 seconds 1 test, 0 failures Randomized with seed 877741
  • 24. HelloWorldTest * test should return a greeting (1.3ms) Finished in 0.03 seconds 1 test, 0 failures Randomized with seed 689156
  • 25. CHAPTER TWO: test the documentation seguire la documentazione scrivendo prima i test
  • 26. defmodule GreetingTest do use ExUnit.Case, async: true test "should return a greeting" do pid = spawn(Greeting, :loop, []) send pid, {self(), :greet} assert_receive "hello world" end end
  • 27. defmodule Greeting do def loop do receive do {from_pid, :greet} -> send from_pid, "hello world" loop() end end end
  • 28. defmodule GreetingTest do use ExUnit.Case, async: true test "should return a greeting" do pid = spawn(Greeting, :loop, []) send pid, {self(), :greet} assert_receive "hello world" end end
  • 29. defmodule GreetingTest do use ExUnit.Case, async: true test "should return a greeting" do pid = Greeting.start response = Greeting.greet(pid) assert "hello world" == response end end
  • 30. defmodule Greeting do def start do spawn(fn -> loop() end) end def greet(pid) do send pid, {self(), :greet} receive do reply -> reply end end ...
  • 31. test "should return a greeting in spanish" do pid = Greeting.start(:spanish) response = Greeting.greet(pid) assert "hola mundo" == response end
  • 32. defmodule Greeting do def start(language nil) do spawn(fn -> loop(language) end) end defp loop(language) do receive do {from_pid, :greet} -> send from_pid, greet_message_for(language) loop(language) end end defp greet_message_for(:spanish), do: "hola mundo" defp greet_message_for(_), do: "hello world" end
  • 33. CHAPTER THREE: the bank account proviamo a scrivere una piccola applicazione
  • 34. The Bank Account Application TODO - Posso creare un nuovo account - Posso eliminare un account - Posso controllare il saldo corrente per ciascun account - Posso depositare un importo - Posso prelevare un importo DOING
  • 35. The Bank Account Application TODO - Posso creare un nuovo account - Posso eliminare un account - Posso controllare il saldo corrente per ciascun account - Posso depositare un importo - Posso prelevare un importo DOING
  • 36. test "should create a new account" do bank_pid = Bank.start() created_response = Bank.create_account(bank_pid, "non_existing_account") assert {:ok, :account_created} == created_response end
  • 37. defmodule Bank do def start() do nil end def create_account(_pid, _name) do {:ok, :account_created} end end
  • 38. test "should return an error when the account exists" do bank_pid = Bank.start() Bank.create_account(bank_pid, "non_existing_account") response = Bank.create_account(bank_pid, "non_existing_account") assert {:error, :account_already_exists} == response end
  • 39. defmodule Bank do def start() do spawn(fn -> loop(%{}) end) end def create_account(pid, name) do send pid, {self(), {:create, name}} receive do reply -> reply end end ... ... defp loop(accounts) do receive do {from_pid, {:create, name}} -> {updated_accounts, message} = _create_account(name, accounts) send from_pid, message loop(updated_accounts) end end defp _create_account(name, accounts) do case Map.has_key?(accounts, account) do true -> {accounts, {:error, :account_already_exists}} _ -> {Map.put(accounts, name, nil), {:ok, :account_created}} end end end
  • 40. test "should delete an existing account" do bank_pid = Bank.start() not_exists_response = Bank.delete_account(bank_pid, "an_account") Bank.create_account(bank_pid, "an_account") deleted_response = Bank.delete_account(bank_pid, "an_account") assert {:error, :account_not_exists} == not_exists_response assert {:ok, :account_deleted} == deleted_response end
  • 41. defp loop(accounts) do receive do {from_pid, message} -> {updated_accounts, reply} = handle_message(message, accounts) send from_pid, reply loop(updated_accounts) end end defp handle_message({:create, name}, accounts) case Map.has_key?(accounts, name) do true -> {accounts, {:error, :account_already_exists}} _ -> {Map.put(accounts, name, nil), {:ok, :account_created}} end end defp handle_message({:delete, name}, accounts) case Map.has_key?(accounts, name) do true -> {Map.delete(accounts, name), {:ok, :account_deleted}} _ -> {accounts, {:error, :account_not_exists}} end end
  • 42. test "should return the current balance of an existing account" do bank_pid = Bank.start() Bank.create_account(bank_pid, "an_account") response = Bank.check_balance(bank_pid, "an_account") assert {:ok, 0} == response end
  • 43. def check_balance(pid, name) do send pid, {self(), {:check_balance, name}} receive do reply -> reply end end defp handle_message({:check_balance, name}, accounts) case Map.has_key?(accounts, name) do false -> {accounts, {:error, :account_not_exists}} _ -> {accounts, {:ok, Map.get(accounts, name)}} end end
  • 44. BankTest * test when account exists we are able to deposit positive amounts (0.04ms) * test when account exists we are not able to withdraw if the amount is greater than current balance (0.01ms) * test when account exists we are able to delete an account (0.01ms) * test when account does not exists we are able to create a new account (0.00ms) * test when account exists we are not able to deposit negative amounts (0.01ms) * test when account exists we are not able to withdraw a negative amount (0.01ms) * test when account does not exists we are not able to check current balance (0.00ms) * test when account does not exists we are not able to deposit (0.00ms) * test when account exists we are able to withdraw if the amount is lower or equal than current balance (0.01ms) * test when account does not exists we are not able to delete an account (0.00ms) * test when account exists we are able to check the current balance (0.01ms) * test when account exists we are not able to create an account with the same name (0.00ms) * test when account exists we are able to perform actions on different accounts (0.02ms) * test when account does not exists we are not able to withdraw (0.00ms) Finished in 0.05 seconds 14 tests, 0 failures
  • 45. Se provassimo a estrarre un processo per ogni account? Questo è refactoring.
  • 46. defp handle_message({:create, name}, accounts) case Map.has_key?(accounts, name) do true -> {accounts, {:error, :account_already_exists}} _ -> bank_account = BankAccount.start() {Map.put(accounts, name, bank_account), {:ok, :account_created}} end end
  • 47. E poi tante altre cose ... Images from http://learnyousomeerlang.com defmodule Bank do use Application def start(_type, _args) do Bank.Supervisor.start_link([]) end end defmodule Bank.Supervisor do use Supervisor def start_link(opts) do Supervisor.start_link(__MODULE__, :ok, opts) end def init(:ok) do children = [ Bank, AccountSupervisor ] Supervisor.init(children, strategy: :one_for_one) end end
  • 48. The Bank Account Application TODO - Posso creare un nuovo account - Posso eliminare un account - Posso controllare il saldo corrente per ciascun account - Posso depositare un importo - Posso prelevare un importo DOING - Voglio esporre una API HTTP
  • 49. The Bank Account Application TODO - Posso creare un nuovo account - Posso eliminare un account - Posso controllare il saldo corrente per ciascun account - Posso depositare un importo - Posso prelevare un importo DOING - Voglio esporre una API HTTP
  • 50. CHAPTER FOUR (the last one): a RESTful API esporre l’applicazione appena creata in RESTful
  • 51. defmodule Http.HelloWorldTest do use ExUnit.Case, async: true use Plug.Test @opts Http.HelloWorld.init([]) test "should return a greeting" do conn = conn(:get, "/greet") |> Http.HelloWorld.call(@opts) assert 200 == conn.status assert "hello world" == conn.resp_body end end
  • 52. defmodule Http.HelloWorld do use Plug.Router plug :match plug :dispatch get "/greet" do send_resp(conn, 200, "hello world") end end
  • 53. defmodule Http.RouterTest do use ExUnit.Case, async: true use Plug.Test import Mock @router Http.Router @opts @router.init([]) test "should return 201 when a new account is created" do with_mock Bank.Admin, [create_account: fn("joe") -> {:ok, :account_created} end] do conn = do_request(:post, "/accounts/joe") assert 201 == conn.status assert "/accounts/joe" == conn.resp_body end end defp do_request(verb, endpoint, payload) do conn(verb, endpoint, payload) |> @router.call(@opts) end end
  • 54. defmodule Http.Router do use Plug.Router plug :match plug :dispatch post "/accounts/:account_name" do {:ok, :account_created} = Bank.Admin.create_account(account_name) send_resp(conn, 201, "/accounts/" <> account_name) end end
  • 55. defmodule Http.RouterTest do use ExUnit.Case, async: true use Plug.Test import Mox @router Http.Router @opts @router.init([]) test "returns 201 when a new account is created" do expect(Bank.AdminMock, :create_account, fn("joe") -> {:ok, :account_created} end) conn = do_request(:post, "/accounts/joe") assert 201 == conn.status assert "/accounts/joe" == conn.resp_body verify! Bank.AdminMock end defp do_request(verb, endpoint, payload) do conn(verb, endpoint, payload) |> @router.call(@opts) end end
  • 56. defmodule Http.Router do use Plug.Router plug :match plug :dispatch @bank_admin Application.get_env(:bank, :bank_admin) post "/accounts/:account_name" do {:ok, :account_created} = @bank_admin.create_account(account_name) send_resp(conn, 201, "/accounts/" <> account_name) end end
  • 57. defmodule Bank.Supervisor do use Supervisor def start_link(opts) do Supervisor.start_link(__MODULE__, :ok, opts) end def init(:ok) do children = [ Bank, AccountSupervisor, Plug.Adapters.Cowboy.child_spec(:http, Http.Router, [], [port: 4000]) ] Supervisor.init(children, strategy: :one_for_one) end end
  • 58. # checkout the project git clone https://github.com/joebew42/elixir_bank_account.git cd elixir_bank_account/ mix test iex -S mix # create an account curl -v -H "auth: joe" -X "POST" http://localhost:4000/accounts/joe # deposit an amount curl -v -H "auth: joe" -X "PUT" http://localhost:4000/accounts/joe/deposit/100 # check current balance curl -v -H "auth: joe" -X "GET" http://localhost:4000/accounts/joe { "current_balance": 1100 } # delete an account curl -v -H "auth: joe" -X "DELETE" http://localhost:4000/accounts/joe
  • 59. rhythm > process > learning > working software Un attimo ...
  • 60. Software Development is a learning process
  • 61.
  • 63. the little warrior recipe: look for continuous improvement through deliberate practice
  • 64. We have to be proud about the way we work and not of what we have built (it’s not about the tool)
  • 65.
  • 67. “Learn to build working software by playing our preferred videogame … … Writing code!” - @joebew42
  • 68. This presentation is released under Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) https://creativecommons.org/licenses/by-nc-sa/3.0/