First Do No Harm: Surgical Refactoring (extended edition)

192
First Do No Harm Surgical Refactoring Nell Shamrell-Harrington @nellshamrell

Transcript of First Do No Harm: Surgical Refactoring (extended edition)

Page 1: First Do No Harm: Surgical Refactoring (extended edition)

First Do No HarmSurgical Refactoring

Nell Shamrell-Harrington@nellshamrell

Page 2: First Do No Harm: Surgical Refactoring (extended edition)

Section I: Refactoring

First Do No Harm: Surgical Refactoring @nellshamrell

Page 3: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

Refactoring is a change and changes can go wrong

What is refactoring?

Page 4: First Do No Harm: Surgical Refactoring (extended edition)

But…it’s not life or death, right?

First Do No Harm: Surgical Refactoring @nellshamrell

Page 5: First Do No Harm: Surgical Refactoring (extended edition)

But…it’s not life or death, right?

First Do No Harm: Surgical Refactoring @nellshamrell

Software is being integrated into…

Page 6: First Do No Harm: Surgical Refactoring (extended edition)

But…it’s not life or death, right?

First Do No Harm: Surgical Refactoring @nellshamrell

Software is being integrated into…

Transportation

Page 7: First Do No Harm: Surgical Refactoring (extended edition)

But…it’s not life or death, right?

First Do No Harm: Surgical Refactoring @nellshamrell

Software is being integrated into…

TransportationEnergy sources

Page 8: First Do No Harm: Surgical Refactoring (extended edition)

But…it’s not life or death, right?

First Do No Harm: Surgical Refactoring @nellshamrell

Software is being integrated into…

TransportationEnergy sourcesMedical Devices

Page 9: First Do No Harm: Surgical Refactoring (extended edition)

So…is refactoring bad, then?

First Do No Harm: Surgical Refactoring @nellshamrell

Page 10: First Do No Harm: Surgical Refactoring (extended edition)

So…is refactoring bad, then?

First Do No Harm: Surgical Refactoring @nellshamrell

Refactoring is neither inherently good OR bad

Page 11: First Do No Harm: Surgical Refactoring (extended edition)

So…is refactoring bad, then?

First Do No Harm: Surgical Refactoring @nellshamrell

How you do it is what matters

Page 12: First Do No Harm: Surgical Refactoring (extended edition)

How should I refactor?

First Do No Harm: Surgical Refactoring @nellshamrell

2 Common Approaches

Page 13: First Do No Harm: Surgical Refactoring (extended edition)

How should I refactor?

First Do No Harm: Surgical Refactoring @nellshamrell

2 Common Approaches1) Edit and Pray

Page 14: First Do No Harm: Surgical Refactoring (extended edition)

How should I refactor?

First Do No Harm: Surgical Refactoring @nellshamrell

2 Common Approaches1) Edit and Pray

2) Cover and Modify

Page 15: First Do No Harm: Surgical Refactoring (extended edition)

How should I refactor?

First Do No Harm: Surgical Refactoring @nellshamrell

2 Common Approaches1) Edit and Pray

2) Cover and Modify- “Working Effectively with Legacy Code”

Page 16: First Do No Harm: Surgical Refactoring (extended edition)

Section II: Surgical Refactoring

First Do No Harm: Surgical Refactoring @nellshamrell

Page 17: First Do No Harm: Surgical Refactoring (extended edition)

What is surgical refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

Change exactly what we INTEND to changeAnd ONLY what we intend to change

Page 18: First Do No Harm: Surgical Refactoring (extended edition)

What is surgical refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

First do no harm!

Page 19: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

Surgical refactoring is a series of good habits that reduce risk

What is surgical refactoring?

Page 20: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

2 categories of refactoring

What is surgical refactoring?

Page 21: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

2 categories of refactoring

What is surgical refactoring?

1) Necessary refactoring

Page 22: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

2 categories of refactoring

What is surgical refactoring?

1) Necessary refactoring2) Cosmetic refactoring

Page 23: First Do No Harm: Surgical Refactoring (extended edition)

What is a necessary refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

Need to add something

Page 24: First Do No Harm: Surgical Refactoring (extended edition)

What is a necessary refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

Need to add somethingCode is too inefficient

Page 25: First Do No Harm: Surgical Refactoring (extended edition)

What is a necessary refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

Need to add somethingCode is too inefficient

Blocked from achieving a business need

Page 26: First Do No Harm: Surgical Refactoring (extended edition)

What is a necessary refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

Necessary refactorings have a moderate to high risk tolerance

Page 27: First Do No Harm: Surgical Refactoring (extended edition)

What is a cosmetic refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

Page 28: First Do No Harm: Surgical Refactoring (extended edition)

What is a cosmetic refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

No business need to change code

Page 29: First Do No Harm: Surgical Refactoring (extended edition)

What is a cosmetic refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

Something about it just bugs usNo business need to change code

Page 30: First Do No Harm: Surgical Refactoring (extended edition)

What is a cosmetic refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

Cosmetic refactorings have a low risk tolerance

Page 31: First Do No Harm: Surgical Refactoring (extended edition)

What about whitespace refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

Page 32: First Do No Harm: Surgical Refactoring (extended edition)

What about whitespace refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

Ultimate cosmetic refactoring

Page 33: First Do No Harm: Surgical Refactoring (extended edition)

What about whitespace refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

Ultimate cosmetic refactoring

Get a style guide (i.e. Github style guide)

Page 34: First Do No Harm: Surgical Refactoring (extended edition)

What about whitespace refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

Ultimate cosmetic refactoring

Get a style guide (i.e. Github style guide)

If whitespace does not violate style guide, leave it alone!

Page 35: First Do No Harm: Surgical Refactoring (extended edition)

What’s involved in surgical refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

Page 36: First Do No Harm: Surgical Refactoring (extended edition)

What’s involved in surgical refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

1) Pre-op: what to do before touching the code

Page 37: First Do No Harm: Surgical Refactoring (extended edition)

What’s involved in surgical refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

1) Pre-op: what to do before touching the code 2) Operation: doing the actual refactoring

Page 38: First Do No Harm: Surgical Refactoring (extended edition)

What’s involved in surgical refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

1) Pre-op: what to do before touching the code 2) Operation: doing the actual refactoring

3) Recovery: verifying the refactor

Page 39: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

Thanks, @mikelorant!

Page 40: First Do No Harm: Surgical Refactoring (extended edition)

Section IV: Pre-Op

First Do No Harm: Surgical Refactoring @nellshamrell

Page 41: First Do No Harm: Surgical Refactoring (extended edition)

What is involved in pre-op?

First Do No Harm: Surgical Refactoring @nellshamrell

Diagnosis (What exactly does the code do?)

Page 42: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

Page 43: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end Calls Ruby’s system method

Page 44: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

endExecutes sed command with some flags

Page 45: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

Performs a substitution

Page 46: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

endIn a series directories and files

Page 47: First Do No Harm: Surgical Refactoring (extended edition)

So…do we know what it does?

First Do No Harm: Surgical Refactoring @nellshamrell

Page 48: First Do No Harm: Surgical Refactoring (extended edition)

So…do we know what it does?

First Do No Harm: Surgical Refactoring @nellshamrell

Map is influenced by our own experiences and expectations

Page 49: First Do No Harm: Surgical Refactoring (extended edition)

So…do we know what it does?

First Do No Harm: Surgical Refactoring @nellshamrell

Only definite way of knowing what the code does is to execute the code itself

Page 50: First Do No Harm: Surgical Refactoring (extended edition)

So…do we know what it does?

First Do No Harm: Surgical Refactoring @nellshamrell

Best way to repeatedly execute the code is through automated tests

Page 51: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

Now what does this system call do?

Page 52: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

let(:do_system_things) { DoSystemThings.new }

Instantiate the class

Page 53: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

let(:do_system_things) { DoSystemThings.new } let(:dir) { ‘something’ }

Sample argument to pass to class

Page 54: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

let(:do_system_things) { DoSystemThings.new } let(:dir) { ‘something’ }

describe ‘making the system call’ do it ‘calls the Ruby#system method’ do

end

end

Page 55: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

let(:do_system_things) { DoSystemThings.new } let(:dir) { ‘something’ }

describe ‘making the system call’ do it ‘calls the Ruby#system method’ do expect(do_system_things)

.to receive(:system).with(anything())

end end

Expect that our instance of the class

Page 56: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

Will receive a system call with any args

let(:do_system_things) { DoSystemThings.new } let(:dir) { ‘something’ }

describe ‘making the system call’ do it ‘calls the Ruby#system method’ do expect(do_system_things)

.to receive(:system).with(anything())

end end

Page 57: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

let(:do_system_things) { DoSystemThings.new } let(:dir) { ‘something’ }

describe ‘making the system call’ do it ‘calls the Ruby#system method’ do expect(do_system_things)

.to receive(:system).with(anything()) do_system_things.do_the_thing(dir)

end end Call the method

Page 58: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

let(:do_system_things) { DoSystemThings.new } let(:dir) { ‘something’ }

describe ‘making the system call’ do it ‘calls the Ruby#system method’ do expect(do_system_things)

.to receive(:system).with(anything()) do_system_things.do_the_thing(dir)

end end

Spec Passes!

Page 59: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

Remove the system call

Page 60: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

Spec Fails!

Page 61: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

Put the system call back

Page 62: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

Spec Passes!

Page 63: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

endWhat return is expected?

Page 64: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end Rdocs: system method returns true when the command executes successfully

Page 65: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

let(:dir) { ‘something’ }

context ‘when the method call is successful’ do it ‘returns true’ do end end

Page 66: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

let(:dir) { ‘something’ }

context ‘when the method call is successful’ do it ‘returns true’ do expect(do_system_things.do_the_thing(dir)).to eq(true)

end end

Expect the return from calling the method on the instance of the class

Page 67: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

let(:dir) { ‘something’ }

context ‘when the method call is successful’ do it ‘returns true’ do expect(do_system_things.do_the_thing(dir)).to eq(true)

end end

To return true

Page 68: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

let(:dir) { ‘something’ }

context ‘when the method call is successful’ do it ‘returns true’ do expect(do_system_things.do_the_thing(dir)).to eq(true)

end end

Spec Fails!

Page 69: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

'sed: directory/*/.rb: No such file or directory'

let(:dir) { ‘something’ }

context ‘when the method call is successful’ do it ‘returns true’ do expect(do_system_things.do_the_thing(dir)).to eq(true)

end end

Page 70: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

context ‘when the method call is successful’ do let(:directory_name) { ‘some_directory’ } let(:sub_directory) { ‘some_sub_directory’ } let(:file) { ‘some_file.rb’ }

it ‘returns true’ do expect(do_system_things.do_the_thing(dir)).to eq(true)

end end

Set the directory, subdirectory, and file names

Page 71: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

context ‘when the method call is successful’ do let(:directory_name) { ‘some_directory’ } let(:sub_directory) { ‘some_sub_directory’ } let(:file) { ‘some_file.rb’ }

before do FileUtils.mkdir_p(File.join(directory_name, sub_directory)) end

it ‘returns true’ do expect(do_system_things.do_the_thing(dir)).to eq(true)

end end

Create directories and sub-directories

Page 72: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

context ‘when the method call is successful’ do let(:directory_name) { ‘some_directory’ } let(:sub_directory) { ‘some_sub_directory’ } let(:file) { ‘some_file.rb’ }

before do FileUtils.mkdir_p(File.join(directory_name, sub_directory)) path = File .join(directory_name,subdirectory_name,file_name) end

it ‘returns true’ do expect(do_system_things.do_the_thing(dir)).to eq(true)

end end

Create the path for the file

Page 73: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

context ‘when the method call is successful’ do let(:directory_name) { ‘some_directory’ } let(:sub_directory) { ‘some_sub_directory’ } let(:file) { ‘some_file.rb’ }

before do FileUtils.mkdir_p(File.join(directory_name, sub_directory)) path = File .join(directory_name,subdirectory_name,file_name) file = File.new(path, ‘w’) file.write(‘look, there is something in this file’) file.close end

it ‘returns true’ do

Create the actual file

Page 74: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

context ‘when the method call is successful’ do let(:directory_name) { ‘some_directory’ } let(:sub_directory) { ‘some_sub_directory’ } let(:file) { ‘some_file.rb’ }

before do FileUtils.mkdir_p(File.join(directory_name, sub_directory)) path = File .join(directory_name,subdirectory_name,file_name) file = File.new(path, ‘w’) file.write(‘look, there is something in this file’) file.close end

it ‘returns true’ do

Spec Passes!

Page 75: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

context ‘when the method call is successful’ do let(:directory_name) { ‘some_directory’ } let(:sub_directory) { ‘some_sub_directory’ } let(:file) { ‘some_file.rb’ }

before do FileUtils.mkdir_p(File.join(directory_name, sub_directory)) path = File .join(directory_name,subdirectory_name,file_name) file = File.new(path, ‘w’) file.write(‘look, there is something in this file’) file.close end

it ‘returns true’ do

That’s a lot of setup code…

Page 76: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

def create_required_directories_and_files(path, filename) FileUtils.mkdir_p(File.join(path)) file = File.new(File.join(path,file_name), ‘w’) file.write(‘look, there is something in this file’) file.close end Takes a path and

file name

Page 77: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

def create_required_directories_and_files(path, filename) FileUtils.mkdir_p(File.join(path)) file = File.new(File.join(path,file_name), ‘w’) file.write(‘look, there is something in this file’) file.close end Makes the directories

Page 78: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

def create_required_directories_and_files(path, filename) FileUtils.mkdir_p(File.join(path)) file = File.new(File.join(path,file_name), ‘w’) file.write(‘look, there is something in this file’) file.close end

Makes the file

Page 79: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

before do create_required_directories_and_files( File.join(directory_name,subdirectory_name), file_name ) end

it ‘returns true’ do expect(do_system_things.do_the_thing(dir)).to eq(true)

end

Call the setup method

Page 80: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

before do create_required_directories_and_files( File.join(directory_name,subdirectory_name), file_name ) end

it ‘returns true’ do expect(do_system_things.do_the_thing(dir)).to eq(true)

end

Create and pass the path for the file

Page 81: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

before do create_required_directories_and_files( File.join(directory_name,subdirectory_name), file_name ) end

it ‘returns true’ do expect(do_system_things.do_the_thing(dir)).to eq(true)

end Pass the file name

Page 82: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

before do create_required_directories_and_files( File.join(directory_name,subdirectory_name), file_name ) end

it ‘returns true’ do expect(do_system_things.do_the_thing(dir)).to eq(true)

end

Spec Passes!

Page 83: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

before do create_required_directories_and_files( File.join(directory_name,subdirectory_name), file_name ) end

it ‘returns true’ do expect(do_system_things.do_the_thing(dir)).to eq(true) end

after do FileUtils.rm_rf(directory_name) end Remove created

directories and file

Page 84: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

before do create_required_directories_and_files( File.join(directory_name,subdirectory_name), file_name ) end

it ‘returns true’ do expect(do_system_things.do_the_thing(dir)).to eq(true) end

after do FileUtils.rm_rf(directory_name) end

Spec Passes!

Page 85: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

*pause*

Page 86: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

sed is a streaming text editor

Page 87: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

Page 88: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

Find match for this pattern

s/:([[:alnum:]]+)[[:space:]]=>/\\1:/g'

Page 89: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

Replace it with this pattern

s/:([[:alnum:]]+)[[:space:]]=>/\\1:/g'

Page 90: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def first_regex_match(string)

end

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

Page 91: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def first_regex_match(string) //.match(string) end

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

Page 92: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

describe ‘what the regex matches’ # FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

end

Page 93: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

describe ‘what the regex matches’ # FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {‘'}

end

Page 94: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

describe ‘what the regex matches’ # FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {''} it 'matches a string' do

end end

Page 95: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

describe ‘what the regex matches’ # FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {''} it 'matches a string' do expect(do_system_things.first_regex_match(string)) .not_to be_nil end end

Page 96: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def first_regex_match(string) //.match(string) end

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

Returns nil if no successful match

Page 97: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

describe ‘what the regex matches’ # FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {''} it 'matches a string' do expect(do_system_things.first_regex_match(string)) .not_to be_nil end end

Means it found a successful match

Page 98: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

describe ‘what the regex matches’ # FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {''} it 'matches a string' do expect(do_system_things.first_regex_match(string)) .not_to be_nil end end

first character is ‘:’

Page 99: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

describe ‘what the regex matches’ # FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {‘:'} it 'matches a string' do expect(do_system_things.first_regex_match(string)) .not_to be_nil end end add ‘:’ to string

Page 100: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

describe ‘what the regex matches’ # FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {‘:'} it 'matches a string' do expect(do_system_things.first_regex_match(string)) .not_to be_nil end end

Spec Fails!

Page 101: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def first_regex_match(string) /:/.match(string) end

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

add ‘:’ to regex

Page 102: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def first_regex_match(string) /:/.match(string) end

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

Spec Passes!

Page 103: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

describe ‘what the regex matches’ # FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {‘:'} it 'matches a string' do expect(do_system_things.first_regex_match(string)) .not_to be_nil end end

Next character is alnum character class

Page 104: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

describe ‘what the regex matches’ # FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {‘:a'} it 'matches a string' do expect(do_system_things.first_regex_match(string)) .not_to be_nil end end Add letter to test string

Page 105: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

Spec Fails!

describe ‘what the regex matches’ # FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {‘:a'} it 'matches a string' do expect(do_system_things.first_regex_match(string)) .not_to be_nil end end

Page 106: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def first_regex_match(string) /:[[:alnum:]]/.match(string) end

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

add alnum to regex

Page 107: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def first_regex_match(string) /:[[:alnum:]]/.match(string) end

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

Spec Passes!

Page 108: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

describe ‘what the regex matches’ # FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {‘:a'} it 'matches a string' do expect(do_system_things.first_regex_match(string)) .not_to be_nil end end Means character must

appear one or more times

Page 109: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

describe ‘what the regex matches’ # FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {‘:ab'} it 'matches a string' do expect(do_system_things.first_regex_match(string)) .not_to be_nil end end

Add character to test string

Page 110: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

Spec Passes!

describe ‘what the regex matches’ # FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {‘:ab'} it 'matches a string' do expect(do_system_things.first_regex_match(string)) .not_to be_nil end end

Page 111: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

But…we were expecting a failure…

describe ‘what the regex matches’ # FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {‘:ab'} it 'matches a string' do expect(do_system_things.first_regex_match(string)) .not_to be_nil end end

Page 112: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def first_regex_match(string) /:[[:alnum:]]/.match(string) end

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end Will return successful match with only one alnum character

Page 113: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

describe ‘what the regex matches’ # FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {‘:ab'} it 'matches a string' do expect(do_system_things.first_regex_match(string) .to_s).to include(‘:ab’) end

end

Examining content of string that was captured by the regex

Page 114: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

Spec Fails!

describe ‘what the regex matches’ # FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {‘:ab'} it 'matches a string' do expect(do_system_things.first_regex_match(string) .to_s).to include(‘:ab’) end

end

Page 115: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def first_regex_match(string) /:[[:alnum:]]+/.match(string) end

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

add + quantifier

Page 116: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def first_regex_match(string) /:[[:alnum:]]+/.match(string) end

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

Spec Passes!

Page 117: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

# FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {‘:ab'} it 'matches a string' do expect(do_system_things.first_regex_match(string) .to_s).to include(‘:ab’) end

Capture group

Page 118: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

# FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {‘:ab'} it 'matches a string' do expect(do_system_things.first_regex_match(string) .to_s).to include(‘:ab’) end

it ‘captures a group’ do

end

Page 119: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

# FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {‘:ab'} it 'matches a string' do expect(do_system_things.first_regex_match(string) .to_s).to include(‘:ab’) end

it ‘captures a group’ do expect(do_system_things.first_regex_match(string)[1]) .not_to be_nil end Testing that match captures

a capture group ([1] references first capture group)

Page 120: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

Spec Fails!

# FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {‘:ab'} it 'matches a string' do expect(do_system_things.first_regex_match(string) .to_s).to include(‘:ab’) end

it ‘captures a group’ do expect(do_system_things.first_regex_match(string)[1]) .not_to be_nil end

Page 121: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def first_regex_match(string) /:[[:alnum:]]+()/.match(string) end

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

endAdding empty capture group

Page 122: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def first_regex_match(string) /:[[:alnum:]]+()/.match(string) end

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

Spec Passes!

Page 123: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

# FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {‘:ab'} it 'matches a string' do expect(do_system_things.first_regex_match(string) .to_s).to include(‘:ab’) end

it ‘captures a group’ do expect(do_system_things.first_regex_match(string)[1]) .not_to be_nil expect(do_system_things.first_regex_match(string)[1]) .to eq(‘ab’) end

Testing content of capture group

Page 124: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rb

Spec Fails!

# FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/

let(:string) {‘:ab'} it 'matches a string' do expect(do_system_things.first_regex_match(string) .to_s).to include(‘:ab’) end

it ‘captures a group’ do expect(do_system_things.first_regex_match(string)[1]) .not_to be_nil expect(do_system_things.first_regex_match(string)[1]) .to eq(‘ab’) end

Page 125: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def first_regex_match(string) /:([[:alnum:]]+)/.match(string) end

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

endPlacing capture group in correct place

Page 126: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def first_regex_match(string) /:([[:alnum:]]+)/.match(string) end

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

Spec Passes!

Page 127: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def first_regex_match(string) /:([[:alnum:]]+)[[:space]]=>/.match(string) end

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end Fast Forward… (Adding in tests for rest of characters)

Page 128: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def first_regex_match(string) /:([[:alnum:]]+)[[:space]]=>/.match(string) end

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end We know what this regex does! ‘:ab =>’ matches!

Page 129: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rbcontext ‘changing the file’ do before do file = File.open(File.join(directory_path,file_name)) file.write(':ab =>') file.close end end

Write sample string to file (note the space and hash rocket)

Page 130: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rbcontext ‘changing the file’ do it ‘changes the file’ do original_contents = File.read(File.join(directory_path,file_name))

end end

Capture original contents of file

Page 131: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rbcontext ‘changing the file’ do it ‘changes the file’ do original_contents = File.read(File.join(directory_path,file_name))

do_system_things.do_the_thing(directory_name)

end end Call the method

Page 132: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rbcontext ‘changing the file’ do it ‘changes the file’ do original_contents = File.read(File.join(directory_path,file_name))

do_system_things.do_the_thing(directory_name)

new_contents = File.read (File.join(directory_path,file_name)) end end

Capture new contents of file

Page 133: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rbcontext ‘changing the file’ do it ‘changes the file’ do original_contents = File.read(File.join(directory_path,file_name))

do_system_things.do_the_thing(directory_name)

new_contents = File.read (File.join(directory_path,file_name)) expect(original_contents).not_to eq(new_contents) end end

Make sure file changes

Page 134: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rbcontext ‘changing the file’ do it ‘changes the file’ do original_contents = File.read(File.join(directory_path,file_name))

do_system_things.do_the_thing(directory_name)

new_contents = File.read (File.join(directory_path,file_name)) expect(original_contents).not_to eq(new_contents) end end

Spec Passes!

Page 135: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

*pause*

Page 136: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

Replace match for the first pattern with this second pattern

s/:([[:alnum:]]+)[[:space:]]=>/\\1:/g'

Page 137: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

\\1:

Page 138: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

\\1:

Escape - so we can use a literal \ as the next character

Page 139: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

\\1:

Uses the result of the first capture group from the first pattern

Page 140: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

\\1:

Adds a literal colon

Page 141: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

s/:([[:alnum:]]+)[[:space:]]=>/\\1:/g'

‘:ab =>’

Page 142: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

s/:([[:alnum:]]+)[[:space:]]=>/\\1:/g'

‘ab:’‘:ab =>’ replace with

Page 143: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rbcontext ‘changing the file’ do let(:orig_string) { “:ab => “} let(:new_string) { “ab: ‘}

it ‘changes the file’ do original_contents = File.read(File.join(directory_path,file_name))

do_system_things.do_the_thing(directory_name)

new_contents = File.read(File.join(directory_path,file_name)) end end

Expected content of file before and after

Page 144: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rbcontext ‘changing the file’ do let(:orig_string) { “:ab => “} let(:new_string) { “ab: ‘}

it ‘changes the file’ do original_contents = File.read(File.join(directory_path,file_name))

do_system_things.do_the_thing(directory_name)

new_contents = File.read(File.join(directory_path,file_name)) expect(new_contents).not_to include(orig_string) expect(new_contents).to include(new_string) end end

Verify contents of modified file

Page 145: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rbcontext ‘changing the file’ do let(:orig_string) { “:ab => “} let(:new_string) { “ab: ‘}

it ‘changes the file’ do original_contents = File.read(File.join(directory_path,file_name))

do_system_things.do_the_thing(directory_name)

new_contents = File.read(File.join(directory_path,file_name)) expect(new_contents).not_to include(orig_string) expect(new_contents).to include(new_string) end end Spec Passes!

Page 146: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

*pause*

Page 147: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

s/:([[:alnum:]]+)[[:space:]]=>/\\1:/g'

Replace all matches for the first pattern with the second pattern

Page 148: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rbcontext ‘changing the file’ do let(:orig_string) { “:ab => :ab =>“} let(:new_string) { “ab: ab:’}

it ‘changes all matches within the file’ do original_contents = File.read(File.join(directory_path,file_name))

do_system_things.do_the_thing(directory_name)

new_contents = File.read(File.join(directory_path,file_name)) expect(new_contents).not_to include(orig_string) expect(new_contents).to include(new_string) end end

Expected content of file before and after

Page 149: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rbcontext ‘changing the file’ do let(:orig_string) { “:ab => :ab =>“} let(:new_string) { “ab: ab:’}

it ‘changes all matches within the file’ do original_contents = File.read(File.join(directory_path,file_name))

do_system_things(directory_name)

new_contents = File.read(File.join(directory_path,file_name)) expect(new_contents).not_to include(orig_string) expect(new_contents).to include(new_string) end end Spec Passes!

Page 150: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' ’s/:([[:alnum:]]+)[[:space:]]=>/\\1:/" #{dir}/**/*.rb" end

end

Taking out the global flag

Page 151: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' ’s/:([[:alnum:]]+)[[:space:]]=>/\\1:/" #{dir}/**/*.rb" end

end

Spec Fails!

Page 152: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' ’s/:([[:alnum:]]+)[[:space:]]=>/\\1:/g" #{dir}/**/*.rb" end

end

Putting the flag back in

Page 153: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' ’s/:([[:alnum:]]+)[[:space:]]=>/\\1:/g" #{dir}/**/*.rb" end

end

Spec Passes!

Page 154: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' ’s/:([[:alnum:]]+)[[:space:]]=>/\\1:/g" #{dir}/**/*.rb" end

end

Next step…add in tests for these flags

Page 155: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) # taking the -E flag out system "sed -i '' ’s/:([[:alnum:]]+)[[:space:]]=>/\\1:/g" #{dir}/**/*.rb" end

end

Page 156: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) # taking the -E flag out system "sed -i '' ’s/:([[:alnum:]]+)[[:space:]]=>/\\1:/g" #{dir}/**/*.rb" end

end

Spec Fails!

Page 157: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) # putting the -E flag back in system "sed E —i '' ’s/:([[:alnum:]]+)[[:space:]]=>/\\1:/g" #{dir}/**/*.rb" end

end

Page 158: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) # putting the -E flag back in system "sed E —i '' ’s/:([[:alnum:]]+)[[:space:]]=>/\\1:/g" #{dir}/**/*.rb" end

end

Page 159: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) # putting the -E flag back in system "sed E —i '' ’s/:([[:alnum:]]+)[[:space:]]=>/\\1:/g" #{dir}/**/*.rb" end

end

Spec Passes!

Page 160: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed E —i '' ’s/:([[:alnum:]]+)[[:space:]]=>/\\1:/g" #{dir}/**/*.rb" end

end

Page 161: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rbcontext ‘changing the file’ do

it ‘does not save a backup copy of the file’ do # Expect the subdirectory we created to contain only one file expect(Dir[“#{directory_name}/#{subdirectory_name}/*”] .count).to eq(1)

end end

Page 162: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rbcontext ‘changing the file’ do

it ‘does not save a backup copy of the file’ do # Expect the subdirectory we created to contain only one file expect(Dir[“#{directory_name}/#{subdirectory_name}/*”] .count).to eq(1)

do_system_things.do_the_thing(directory_name)

end end

Page 163: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rbcontext ‘changing the file’ do

it ‘does not save a backup copy of the file’ do # Expect the subdirectory we created to contain only one file expect(Dir[“#{directory_name}/#{subdirectory_name}/*”] .count).to eq(1)

do_system_things.do_the_thing(directory_name)

expect(Dir[“#{directory_name}/#{subdirectory_name}/*”] .count).to eq(1) end end

Page 164: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

spec/do_system_things_spec.rbcontext ‘changing the file’ do

it ‘does not save a backup copy of the file’ do # Expect the subdirectory we created to contain only one file expect(Dir[“#{directory_name}/#{subdirectory_name}/*”] .count).to eq(1)

do_system_things.do_the_thing(directory_name)

expect(Dir[“#{directory_name}/#{subdirectory_name}/*”] .count).to eq(1) end end

Spec Passes!

Page 165: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) # adding an extension to the -i flag system "sed E —i .tmp ’s/:([[:alnum:]]+)[[:space:]]=>/\\1:/g" #{dir}/**/*.rb" end

end

Page 166: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) # adding an extension to the -i flag system "sed E —i .tmp ’s/:([[:alnum:]]+)[[:space:]]=>/\\1:/g" #{dir}/**/*.rb” end

end

Spec Fails!

Page 167: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) # removing extension to -i flag system "sed E —i ‘’ ’s/:([[:alnum:]]+)[[:space:]]=>/\\1:/g" #{dir}/**/*.rb" end

end

Page 168: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) # removing extension to -i flag system "sed E —i ‘’ ’s/:([[:alnum:]]+)[[:space:]]=>/\\1:/g" #{dir}/**/*.rb" end

end

Spec Passes!

Page 169: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' ’s/:([[:alnum:]]+)[[:space:]]=>/\\1:/g" #{dir}/**/*.rb" end

end

We know what this does!

Page 170: First Do No Harm: Surgical Refactoring (extended edition)

Section V: Operation

First Do No Harm: Surgical Refactoring @nellshamrell

Page 171: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

Page 172: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

end

Page 173: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' #{dir}/**/*.rb" end

private

def substitute_command 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' end

end

Page 174: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' #{substitute_command} #{dir}/**/*.rb" end

private

def substitute_command 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' end

end

Page 175: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' #{substitute_command} #{dir}/**/*.rb" end

private

def substitute_command 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' end

end

Specs Pass!

Page 176: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' #{substitute_command} #{dir}/**/*.rb" end

private

def substitute_command 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' end

end Really old regex syntax

Page 177: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' #{substitute_command} #{dir}/**/*.rb" end

private

def substitute_command ’s/:(\w+)\s=>/\\1:/g’ end

end New regex syntax

Page 178: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' #{substitute_command} #{dir}/**/*.rb" end

private

def substitute_command ’s/:(\w+)\s=>/\\1:/g’ end

end Specs Fail!

\w != [:alnum]

Page 179: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' #{substitute_command} #{dir}/**/*.rb" end

private

def substitute_command 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' end

end Back to the old regex syntax

Page 180: First Do No Harm: Surgical Refactoring (extended edition)

First Do No Harm: Surgical Refactoring @nellshamrell

lib/do_system_things.rb

class DoSystemThings

def do_the_thing(dir) system "sed -E -i '' #{substitute_command} #{dir}/**/*.rb" end

private

def substitute_command 's/:([[:alnum:]]+)[[:space:]]=>/\\1:/g' end

end

Specs Pass!

Page 181: First Do No Harm: Surgical Refactoring (extended edition)

Section VI: Recovery

First Do No Harm: Surgical Refactoring @nellshamrell

Page 182: First Do No Harm: Surgical Refactoring (extended edition)

What do I need to do after refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

Need to know two things decisively

Page 183: First Do No Harm: Surgical Refactoring (extended edition)

What do I need to do after refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

Need to know two things decisively 1) Does the behavior still exist?

Page 184: First Do No Harm: Surgical Refactoring (extended edition)

What do I need to do after refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

Need to know two things decisively 1) Does the behavior still exist?

2) Is it connected correctly?

Page 185: First Do No Harm: Surgical Refactoring (extended edition)

What do I need to do after refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

What about QA?

Page 186: First Do No Harm: Surgical Refactoring (extended edition)

What do I need to do after refactoring?

First Do No Harm: Surgical Refactoring @nellshamrell

Ideally, QA should find nothing

Page 187: First Do No Harm: Surgical Refactoring (extended edition)

What if something goes wrong?

First Do No Harm: Surgical Refactoring @nellshamrell

Page 188: First Do No Harm: Surgical Refactoring (extended edition)

What if something goes wrong?

First Do No Harm: Surgical Refactoring @nellshamrell

1) Take responsibility - what your code does in production is your responsibility

Page 189: First Do No Harm: Surgical Refactoring (extended edition)

What if something goes wrong?

First Do No Harm: Surgical Refactoring @nellshamrell

1) Take responsibility - what your code does in production is your responsibility

2) Evaluate risk of another change (a change to a change is still a change)

Page 190: First Do No Harm: Surgical Refactoring (extended edition)

What if something goes wrong?

First Do No Harm: Surgical Refactoring @nellshamrell

1) Take responsibility - what your code does in production is your responsibility

2) Evaluate risk of another change (a change to a change is still a change)

3) Fix the problem (if you can’t, find someone who can!)

Page 191: First Do No Harm: Surgical Refactoring (extended edition)

Conclusion

First Do No Harm: Surgical Refactoring @nellshamrell

“I’m not a great programmer, I’m just a good programmer with great habits”

- “Refactoring: Ruby Edition”

Page 192: First Do No Harm: Surgical Refactoring (extended edition)

Thank You!

First Do No Harm: Surgical Refactoring @nellshamrell

Nell Shamrell-Harrington

Software Development Engineer at Chef

@nellshamrell

www.nellshamrell.com

[email protected]