SlideShare a Scribd company logo
1 of 191
Download to read offline
First Do No Harm
Surgical Refactoring
Nell Shamrell-Harrington
@nellshamrell
Section I:
Refactoring
First Do No Harm: Surgical Refactoring @nellshamrell
First Do No Harm: Surgical Refactoring @nellshamrell
Refactoring is a change
and changes can go wrong
What is refactoring?
But…it’s not life or death, right?
First Do No Harm: Surgical Refactoring @nellshamrell
But…it’s not life or death, right?
First Do No Harm: Surgical Refactoring @nellshamrell
Software is being integrated into…
But…it’s not life or death, right?
First Do No Harm: Surgical Refactoring @nellshamrell
Software is being integrated into…
Transportation
But…it’s not life or death, right?
First Do No Harm: Surgical Refactoring @nellshamrell
Software is being integrated into…
Transportation
Energy sources
But…it’s not life or death, right?
First Do No Harm: Surgical Refactoring @nellshamrell
Software is being integrated into…
Transportation
Energy sources
Medical Devices
So…is refactoring bad, then?
First Do No Harm: Surgical Refactoring @nellshamrell
So…is refactoring bad, then?
First Do No Harm: Surgical Refactoring @nellshamrell
Refactoring is neither inherently good OR bad
So…is refactoring bad, then?
First Do No Harm: Surgical Refactoring @nellshamrell
How you do it is what matters
How should I refactor?
First Do No Harm: Surgical Refactoring @nellshamrell
2 Common Approaches
How should I refactor?
First Do No Harm: Surgical Refactoring @nellshamrell
2 Common Approaches
1) Edit and Pray
How should I refactor?
First Do No Harm: Surgical Refactoring @nellshamrell
2 Common Approaches
1) Edit and Pray
2) Cover and Modify
How should I refactor?
First Do No Harm: Surgical Refactoring @nellshamrell
2 Common Approaches
1) Edit and Pray
2) Cover and Modify
- “Working Effectively with Legacy Code”
Section II:
Surgical Refactoring
First Do No Harm: Surgical Refactoring @nellshamrell
What is surgical refactoring?
First Do No Harm: Surgical Refactoring @nellshamrell
Change exactly what we INTEND to change
And ONLY what we intend to change
What is surgical refactoring?
First Do No Harm: Surgical Refactoring @nellshamrell
First do no harm!
First Do No Harm: Surgical Refactoring @nellshamrell
Surgical refactoring is a series of
good habits that reduce risk
What is surgical refactoring?
First Do No Harm: Surgical Refactoring @nellshamrell
2 categories of refactoring
What is surgical refactoring?
First Do No Harm: Surgical Refactoring @nellshamrell
2 categories of refactoring
What is surgical refactoring?
1) Necessary refactoring
First Do No Harm: Surgical Refactoring @nellshamrell
2 categories of refactoring
What is surgical refactoring?
1) Necessary refactoring
2) Cosmetic refactoring
What is a necessary refactoring?
First Do No Harm: Surgical Refactoring @nellshamrell
Need to add something
What is a necessary refactoring?
First Do No Harm: Surgical Refactoring @nellshamrell
Need to add something
Code is too inefficient
What is a necessary refactoring?
First Do No Harm: Surgical Refactoring @nellshamrell
Need to add something
Code is too inefficient
Blocked from achieving a business need
What is a necessary refactoring?
First Do No Harm: Surgical Refactoring @nellshamrell
Necessary refactorings have a
moderate to high risk tolerance
What is a cosmetic refactoring?
First Do No Harm: Surgical Refactoring @nellshamrell
What is a cosmetic refactoring?
First Do No Harm: Surgical Refactoring @nellshamrell
No business need to change code
What is a cosmetic refactoring?
First Do No Harm: Surgical Refactoring @nellshamrell
Something about it just bugs us
No business need to change code
What is a cosmetic refactoring?
First Do No Harm: Surgical Refactoring @nellshamrell
Cosmetic refactorings
have a low risk tolerance
What about whitespace refactoring?
First Do No Harm: Surgical Refactoring @nellshamrell
What about whitespace refactoring?
First Do No Harm: Surgical Refactoring @nellshamrell
Ultimate cosmetic refactoring
What about whitespace refactoring?
First Do No Harm: Surgical Refactoring @nellshamrell
Ultimate cosmetic refactoring
Get a style guide
(i.e. Github style guide)
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!
What’s involved in surgical refactoring?
First Do No Harm: Surgical Refactoring @nellshamrell
What’s involved in surgical refactoring?
First Do No Harm: Surgical Refactoring @nellshamrell
1) Pre-op: what to do before touching the code
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
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
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!
Section IV:
Pre-Op
First Do No Harm: Surgical Refactoring @nellshamrell
What is involved in pre-op?
First Do No Harm: Surgical Refactoring @nellshamrell
Diagnosis
(What exactly does the code do?)
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
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
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
Executes sed command with
some flags
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
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
In a series directories and files
So…do we know what it does?
First Do No Harm: Surgical Refactoring @nellshamrell
So…do we know what it does?
First Do No Harm: Surgical Refactoring @nellshamrell
Map is influenced by our own
experiences and expectations
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
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
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?
First Do No Harm: Surgical Refactoring @nellshamrell
spec/do_system_things_spec.rb
let(:do_system_things) { DoSystemThings.new }
Instantiate the class
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
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
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
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
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
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!
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
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!
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
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!
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
What return is expected?
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 call returns true
when the command executes
successfully
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
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
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
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!
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
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
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
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
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
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!
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…
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
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
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
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
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
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
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!
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
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!
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*
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
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
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
Find match for this pattern
s/:([[:alnum:]]+)[[:space:]]=>/1:/g'
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
Replace it with this pattern
s/:([[:alnum:]]+)[[:space:]]=>/1:/g'
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
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
First Do No Harm: Surgical Refactoring @nellshamrell
spec/do_system_things_spec.rb
describe ‘what the regex matches’
# FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/
end
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
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
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 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
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
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 ‘:’
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
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!
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
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!
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
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
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
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
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!
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
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
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
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
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 character
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
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
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
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!
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
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
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)
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
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
Adding empty capture group
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!
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
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
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
Placing capture group in
correct place
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!
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…
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!
First Do No Harm: Surgical Refactoring @nellshamrell
spec/do_system_things_spec.rb
context ‘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
First Do No Harm: Surgical Refactoring @nellshamrell
spec/do_system_things_spec.rb
context ‘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
First Do No Harm: Surgical Refactoring @nellshamrell
spec/do_system_things_spec.rb
context ‘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
First Do No Harm: Surgical Refactoring @nellshamrell
spec/do_system_things_spec.rb
context ‘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
First Do No Harm: Surgical Refactoring @nellshamrell
spec/do_system_things_spec.rb
context ‘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
First Do No Harm: Surgical Refactoring @nellshamrell
spec/do_system_things_spec.rb
context ‘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!
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*
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'
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
1:
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
1:
Escape - so we can use a literal 
as the next character
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
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
1:
Adds a literal colon
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
s/:([[:alnum:]]+)[[:space:]]=>/1:/g'
‘:ab =>’
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
s/:([[:alnum:]]+)[[:space:]]=>/1:/g'
‘ab:’‘:ab =>’ replace with
First Do No Harm: Surgical Refactoring @nellshamrell
spec/do_system_things_spec.rb
context ‘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
First Do No Harm: Surgical Refactoring @nellshamrell
spec/do_system_things_spec.rb
context ‘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
First Do No Harm: Surgical Refactoring @nellshamrell
spec/do_system_things_spec.rb
context ‘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!
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*
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
First Do No Harm: Surgical Refactoring @nellshamrell
spec/do_system_things_spec.rb
context ‘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
First Do No Harm: Surgical Refactoring @nellshamrell
spec/do_system_things_spec.rb
context ‘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!
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
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!
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
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!
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
class DoSystemThings
def do_the_thing(dir)
system "sed -i '' ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g"
#{dir}/**/*.rb"
end
end
Take -E flag out
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
class DoSystemThings
def do_the_thing(dir)
system "sed -i '' ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g"
#{dir}/**/*.rb"
end
end
Spec Fails!
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 -E flag back in
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!
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
-i flag: sed alters
files in place
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
-i flag: sed alters
files in place
Passing an empty
extension
First Do No Harm: Surgical Refactoring @nellshamrell
spec/do_system_things_spec.rb
context ‘changing the file’ do
it ‘does not save a backup copy of the file’ do
expect
(Dir[“#{directory_name}/#{subdirectory_name}/*”]
.count).to eq(1)
end
end
Expect only one file in
directory/subdirectory
path
First Do No Harm: Surgical Refactoring @nellshamrell
spec/do_system_things_spec.rb
context ‘changing the file’ do
it ‘does not save a backup copy of the file’ do
expect
(Dir[“#{directory_name}/#{subdirectory_name}/*”]
.count).to eq(1)
do_system_things.do_the_thing(directory_name)
end
end
Call the method
First Do No Harm: Surgical Refactoring @nellshamrell
spec/do_system_things_spec.rb
context ‘changing the file’ do
it ‘does not save a backup copy of the file’ do
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
Expect only one file in
directory/subdirectory
path
First Do No Harm: Surgical Refactoring @nellshamrell
spec/do_system_things_spec.rb
context ‘changing the file’ do
it ‘does not save a backup copy of the file’ do
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!
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
class DoSystemThings
def do_the_thing(dir)
system "sed -E -i .tmp ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g"
#{dir}/**/*.rb"
end
end
Pass extension to -i flag
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
class DoSystemThings
def do_the_thing(dir)
system "sed -E -i .tmp ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g"
#{dir}/**/*.rb"
end
end
Spec Fails!
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
Passing an empty
extension again
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!
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!
Section V:
Operation
First Do No Harm: Surgical Refactoring @nellshamrell
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
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
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
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
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
Spec Passes!
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
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
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 Spec Fails!
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
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
Spec Passes!
Section VI:
Recovery
First Do No Harm: Surgical Refactoring @nellshamrell
What do I need to do after refactoring?
First Do No Harm: Surgical Refactoring @nellshamrell
Need to know two things decisively
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?
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?
What do I need to do after refactoring?
First Do No Harm: Surgical Refactoring @nellshamrell
What about QA?
What do I need to do after refactoring?
First Do No Harm: Surgical Refactoring @nellshamrell
Ideally, QA should find nothing
What if something goes wrong?
First Do No Harm: Surgical Refactoring @nellshamrell
What if something goes wrong?
First Do No Harm: Surgical Refactoring @nellshamrell
1) Take responsibility - what your code does
in production is your responsibility
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)
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!)
Thank You!
First Do No Harm: Surgical Refactoring @nellshamrell
Nell Shamrell-Harrington
Software Development Engineer at Chef
@nellshamrell
www.nellshamrell.com
nshamrell@chef.io
Thank You!
First Do No Harm: Surgical Refactoring @nellshamrell
Nell Shamrell-Harrington
Software Development Engineer at Chef
@nellshamrell
www.nellshamrell.com
nshamrell@chef.io
Thank you
Jim and Jen!!!!

More Related Content

Viewers also liked (7)

20150911102025
2015091110202520150911102025
20150911102025
 
Presentation1
Presentation1Presentation1
Presentation1
 
Apostila html e css
Apostila html e cssApostila html e css
Apostila html e css
 
Food signs in radiology
Food signs in radiologyFood signs in radiology
Food signs in radiology
 
DevOps Picc12 Management Talk
DevOps Picc12 Management TalkDevOps Picc12 Management Talk
DevOps Picc12 Management Talk
 
S3とSNSで動画機能をつくる話
S3とSNSで動画機能をつくる話S3とSNSで動画機能をつくる話
S3とSNSで動画機能をつくる話
 
Continuous Delivery & DevOps - IT Value Stream Improvements Roadmap Chapter 2 v8
Continuous Delivery & DevOps - IT Value Stream Improvements Roadmap Chapter 2 v8Continuous Delivery & DevOps - IT Value Stream Improvements Roadmap Chapter 2 v8
Continuous Delivery & DevOps - IT Value Stream Improvements Roadmap Chapter 2 v8
 

Similar to First Do No Harm: Surgical Refactoring

Similar to First Do No Harm: Surgical Refactoring (11)

The limits of unit testing by Craig Stuntz
The limits of unit testing by Craig StuntzThe limits of unit testing by Craig Stuntz
The limits of unit testing by Craig Stuntz
 
The Limits of Unit Testing by Craig Stuntz
The Limits of Unit Testing by Craig StuntzThe Limits of Unit Testing by Craig Stuntz
The Limits of Unit Testing by Craig Stuntz
 
Stand back; I'm going to try Scientist!
Stand back; I'm going to try Scientist!Stand back; I'm going to try Scientist!
Stand back; I'm going to try Scientist!
 
Model-checking for efficient malware detection
Model-checking for efficient malware detectionModel-checking for efficient malware detection
Model-checking for efficient malware detection
 
Simple Essay Example Amat
Simple Essay Example  AmatSimple Essay Example  Amat
Simple Essay Example Amat
 
Characteristics of PVS-Studio Analyzer by the Example of EFL Core Libraries, ...
Characteristics of PVS-Studio Analyzer by the Example of EFL Core Libraries, ...Characteristics of PVS-Studio Analyzer by the Example of EFL Core Libraries, ...
Characteristics of PVS-Studio Analyzer by the Example of EFL Core Libraries, ...
 
Art_of_DefectReporting080214
Art_of_DefectReporting080214Art_of_DefectReporting080214
Art_of_DefectReporting080214
 
Quality tools
Quality toolsQuality tools
Quality tools
 
Rtt preso
Rtt presoRtt preso
Rtt preso
 
SRE Lessons for the Enterprise
SRE Lessons for the Enterprise SRE Lessons for the Enterprise
SRE Lessons for the Enterprise
 
Normal accidents and outpatient surgeries
Normal accidents and outpatient surgeriesNormal accidents and outpatient surgeries
Normal accidents and outpatient surgeries
 

More from Nell Shamrell-Harrington

More from Nell Shamrell-Harrington (20)

This Week in Rust: 400 Issues and Counting!
This Week in Rust: 400 Issues and Counting!This Week in Rust: 400 Issues and Counting!
This Week in Rust: 400 Issues and Counting!
 
The Rust Borrow Checker
The Rust Borrow CheckerThe Rust Borrow Checker
The Rust Borrow Checker
 
Higher. Faster. Stronger. Your Applications with Habitat
Higher. Faster. Stronger. Your Applications with HabitatHigher. Faster. Stronger. Your Applications with Habitat
Higher. Faster. Stronger. Your Applications with Habitat
 
Habitat Service Discovery
Habitat Service DiscoveryHabitat Service Discovery
Habitat Service Discovery
 
Web Operations101
Web Operations101Web Operations101
Web Operations101
 
Rust Traits And You: A Deep Dive
Rust Traits And You: A Deep DiveRust Traits And You: A Deep Dive
Rust Traits And You: A Deep Dive
 
Rust, Redis, and Protobuf - Oh My!
Rust, Redis, and Protobuf - Oh My!Rust, Redis, and Protobuf - Oh My!
Rust, Redis, and Protobuf - Oh My!
 
Containers, Virtual Machines, and Bare Metal, Oh My!
Containers, Virtual Machines, and Bare Metal, Oh My!Containers, Virtual Machines, and Bare Metal, Oh My!
Containers, Virtual Machines, and Bare Metal, Oh My!
 
Chef Vault: A Deep Dive
Chef Vault: A Deep DiveChef Vault: A Deep Dive
Chef Vault: A Deep Dive
 
Open Source Governance 101
Open Source Governance 101Open Source Governance 101
Open Source Governance 101
 
DevOps in Politics
DevOps in PoliticsDevOps in Politics
DevOps in Politics
 
Open Source Governance - The Hard Parts
Open Source Governance - The Hard PartsOpen Source Governance - The Hard Parts
Open Source Governance - The Hard Parts
 
Creating Packages that Run Anywhere with Chef Habitat
Creating Packages that Run Anywhere with Chef HabitatCreating Packages that Run Anywhere with Chef Habitat
Creating Packages that Run Anywhere with Chef Habitat
 
Refactoring terraform
Refactoring terraformRefactoring terraform
Refactoring terraform
 
Refactoring Infrastructure Code
Refactoring Infrastructure CodeRefactoring Infrastructure Code
Refactoring Infrastructure Code
 
Devops: A History
Devops: A HistoryDevops: A History
Devops: A History
 
First Do No Harm: Surgical Refactoring (extended edition)
First Do No Harm: Surgical Refactoring (extended edition)First Do No Harm: Surgical Refactoring (extended edition)
First Do No Harm: Surgical Refactoring (extended edition)
 
A Supermarket of Your Own: Running a Private Chef Supermarket
A Supermarket of Your Own: Running a Private Chef SupermarketA Supermarket of Your Own: Running a Private Chef Supermarket
A Supermarket of Your Own: Running a Private Chef Supermarket
 
Public Supermarket: The Insider's Tour
Public Supermarket: The Insider's TourPublic Supermarket: The Insider's Tour
Public Supermarket: The Insider's Tour
 
Beneath the Surface - Rubyconf 2013
Beneath the Surface - Rubyconf 2013Beneath the Surface - Rubyconf 2013
Beneath the Surface - Rubyconf 2013
 

Recently uploaded

Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
WSO2
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 
+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@
 

Recently uploaded (20)

CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistan
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
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
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
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
 
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with Milvus
 
+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...
 

First Do No Harm: Surgical Refactoring

  • 1. First Do No Harm Surgical Refactoring Nell Shamrell-Harrington @nellshamrell
  • 2. Section I: Refactoring First Do No Harm: Surgical Refactoring @nellshamrell
  • 3. First Do No Harm: Surgical Refactoring @nellshamrell Refactoring is a change and changes can go wrong What is refactoring?
  • 4. But…it’s not life or death, right? First Do No Harm: Surgical Refactoring @nellshamrell
  • 5. But…it’s not life or death, right? First Do No Harm: Surgical Refactoring @nellshamrell Software is being integrated into…
  • 6. But…it’s not life or death, right? First Do No Harm: Surgical Refactoring @nellshamrell Software is being integrated into… Transportation
  • 7. But…it’s not life or death, right? First Do No Harm: Surgical Refactoring @nellshamrell Software is being integrated into… Transportation Energy sources
  • 8. But…it’s not life or death, right? First Do No Harm: Surgical Refactoring @nellshamrell Software is being integrated into… Transportation Energy sources Medical Devices
  • 9. So…is refactoring bad, then? First Do No Harm: Surgical Refactoring @nellshamrell
  • 10. So…is refactoring bad, then? First Do No Harm: Surgical Refactoring @nellshamrell Refactoring is neither inherently good OR bad
  • 11. So…is refactoring bad, then? First Do No Harm: Surgical Refactoring @nellshamrell How you do it is what matters
  • 12. How should I refactor? First Do No Harm: Surgical Refactoring @nellshamrell 2 Common Approaches
  • 13. How should I refactor? First Do No Harm: Surgical Refactoring @nellshamrell 2 Common Approaches 1) Edit and Pray
  • 14. How should I refactor? First Do No Harm: Surgical Refactoring @nellshamrell 2 Common Approaches 1) Edit and Pray 2) Cover and Modify
  • 15. How should I refactor? First Do No Harm: Surgical Refactoring @nellshamrell 2 Common Approaches 1) Edit and Pray 2) Cover and Modify - “Working Effectively with Legacy Code”
  • 16. Section II: Surgical Refactoring First Do No Harm: Surgical Refactoring @nellshamrell
  • 17. What is surgical refactoring? First Do No Harm: Surgical Refactoring @nellshamrell Change exactly what we INTEND to change And ONLY what we intend to change
  • 18. What is surgical refactoring? First Do No Harm: Surgical Refactoring @nellshamrell First do no harm!
  • 19. First Do No Harm: Surgical Refactoring @nellshamrell Surgical refactoring is a series of good habits that reduce risk What is surgical refactoring?
  • 20. First Do No Harm: Surgical Refactoring @nellshamrell 2 categories of refactoring What is surgical refactoring?
  • 21. First Do No Harm: Surgical Refactoring @nellshamrell 2 categories of refactoring What is surgical refactoring? 1) Necessary refactoring
  • 22. First Do No Harm: Surgical Refactoring @nellshamrell 2 categories of refactoring What is surgical refactoring? 1) Necessary refactoring 2) Cosmetic refactoring
  • 23. What is a necessary refactoring? First Do No Harm: Surgical Refactoring @nellshamrell Need to add something
  • 24. What is a necessary refactoring? First Do No Harm: Surgical Refactoring @nellshamrell Need to add something Code is too inefficient
  • 25. What is a necessary refactoring? First Do No Harm: Surgical Refactoring @nellshamrell Need to add something Code is too inefficient Blocked from achieving a business need
  • 26. What is a necessary refactoring? First Do No Harm: Surgical Refactoring @nellshamrell Necessary refactorings have a moderate to high risk tolerance
  • 27. What is a cosmetic refactoring? First Do No Harm: Surgical Refactoring @nellshamrell
  • 28. What is a cosmetic refactoring? First Do No Harm: Surgical Refactoring @nellshamrell No business need to change code
  • 29. What is a cosmetic refactoring? First Do No Harm: Surgical Refactoring @nellshamrell Something about it just bugs us No business need to change code
  • 30. What is a cosmetic refactoring? First Do No Harm: Surgical Refactoring @nellshamrell Cosmetic refactorings have a low risk tolerance
  • 31. What about whitespace refactoring? First Do No Harm: Surgical Refactoring @nellshamrell
  • 32. What about whitespace refactoring? First Do No Harm: Surgical Refactoring @nellshamrell Ultimate cosmetic refactoring
  • 33. What about whitespace refactoring? First Do No Harm: Surgical Refactoring @nellshamrell Ultimate cosmetic refactoring Get a style guide (i.e. Github style guide)
  • 34. 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!
  • 35. What’s involved in surgical refactoring? First Do No Harm: Surgical Refactoring @nellshamrell
  • 36. What’s involved in surgical refactoring? First Do No Harm: Surgical Refactoring @nellshamrell 1) Pre-op: what to do before touching the code
  • 37. 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
  • 38. 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
  • 39. 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!
  • 40. Section IV: Pre-Op First Do No Harm: Surgical Refactoring @nellshamrell
  • 41. What is involved in pre-op? First Do No Harm: Surgical Refactoring @nellshamrell Diagnosis (What exactly does the code do?)
  • 42. 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
  • 43. 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
  • 44. 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 Executes sed command with some flags
  • 45. 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
  • 46. 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 In a series directories and files
  • 47. So…do we know what it does? First Do No Harm: Surgical Refactoring @nellshamrell
  • 48. So…do we know what it does? First Do No Harm: Surgical Refactoring @nellshamrell Map is influenced by our own experiences and expectations
  • 49. 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
  • 50. 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
  • 51. 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?
  • 52. First Do No Harm: Surgical Refactoring @nellshamrell spec/do_system_things_spec.rb let(:do_system_things) { DoSystemThings.new } Instantiate the class
  • 53. 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
  • 54. 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
  • 55. 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
  • 56. 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
  • 57. 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
  • 58. 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!
  • 59. 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
  • 60. 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!
  • 61. 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
  • 62. 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!
  • 63. 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 What return is expected?
  • 64. 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 call returns true when the command executes successfully
  • 65. 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
  • 66. 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
  • 67. 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
  • 68. 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!
  • 69. 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
  • 70. 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
  • 71. 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
  • 72. 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
  • 73. 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
  • 74. 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!
  • 75. 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…
  • 76. 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
  • 77. 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
  • 78. 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
  • 79. 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
  • 80. 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
  • 81. 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
  • 82. 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!
  • 83. 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
  • 84. 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!
  • 85. 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*
  • 86. 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
  • 87. 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
  • 88. First Do No Harm: Surgical Refactoring @nellshamrell lib/do_system_things.rb Find match for this pattern s/:([[:alnum:]]+)[[:space:]]=>/1:/g'
  • 89. First Do No Harm: Surgical Refactoring @nellshamrell lib/do_system_things.rb Replace it with this pattern s/:([[:alnum:]]+)[[:space:]]=>/1:/g'
  • 90. 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
  • 91. 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
  • 92. First Do No Harm: Surgical Refactoring @nellshamrell spec/do_system_things_spec.rb describe ‘what the regex matches’ # FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/ end
  • 93. 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
  • 94. 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
  • 95. 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
  • 96. 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
  • 97. 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
  • 98. 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 ‘:’
  • 99. 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
  • 100. 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!
  • 101. 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
  • 102. 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!
  • 103. 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
  • 104. 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
  • 105. 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
  • 106. 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
  • 107. 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!
  • 108. 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
  • 109. 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
  • 110. 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
  • 111. 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
  • 112. 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 character
  • 113. 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
  • 114. 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
  • 115. 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
  • 116. 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!
  • 117. 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
  • 118. 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
  • 119. 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)
  • 120. 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
  • 121. 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 Adding empty capture group
  • 122. 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!
  • 123. 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
  • 124. 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
  • 125. 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 Placing capture group in correct place
  • 126. 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!
  • 127. 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…
  • 128. 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!
  • 129. First Do No Harm: Surgical Refactoring @nellshamrell spec/do_system_things_spec.rb context ‘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
  • 130. First Do No Harm: Surgical Refactoring @nellshamrell spec/do_system_things_spec.rb context ‘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
  • 131. First Do No Harm: Surgical Refactoring @nellshamrell spec/do_system_things_spec.rb context ‘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
  • 132. First Do No Harm: Surgical Refactoring @nellshamrell spec/do_system_things_spec.rb context ‘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
  • 133. First Do No Harm: Surgical Refactoring @nellshamrell spec/do_system_things_spec.rb context ‘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
  • 134. First Do No Harm: Surgical Refactoring @nellshamrell spec/do_system_things_spec.rb context ‘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!
  • 135. 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*
  • 136. 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'
  • 137. First Do No Harm: Surgical Refactoring @nellshamrell lib/do_system_things.rb 1:
  • 138. First Do No Harm: Surgical Refactoring @nellshamrell lib/do_system_things.rb 1: Escape - so we can use a literal as the next character
  • 139. 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
  • 140. First Do No Harm: Surgical Refactoring @nellshamrell lib/do_system_things.rb 1: Adds a literal colon
  • 141. First Do No Harm: Surgical Refactoring @nellshamrell lib/do_system_things.rb s/:([[:alnum:]]+)[[:space:]]=>/1:/g' ‘:ab =>’
  • 142. First Do No Harm: Surgical Refactoring @nellshamrell lib/do_system_things.rb s/:([[:alnum:]]+)[[:space:]]=>/1:/g' ‘ab:’‘:ab =>’ replace with
  • 143. First Do No Harm: Surgical Refactoring @nellshamrell spec/do_system_things_spec.rb context ‘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
  • 144. First Do No Harm: Surgical Refactoring @nellshamrell spec/do_system_things_spec.rb context ‘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
  • 145. First Do No Harm: Surgical Refactoring @nellshamrell spec/do_system_things_spec.rb context ‘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!
  • 146. 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*
  • 147. 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
  • 148. First Do No Harm: Surgical Refactoring @nellshamrell spec/do_system_things_spec.rb context ‘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
  • 149. First Do No Harm: Surgical Refactoring @nellshamrell spec/do_system_things_spec.rb context ‘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!
  • 150. 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
  • 151. 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!
  • 152. 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
  • 153. 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!
  • 154. First Do No Harm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def do_the_thing(dir) system "sed -i '' ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g" #{dir}/**/*.rb" end end Take -E flag out
  • 155. First Do No Harm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def do_the_thing(dir) system "sed -i '' ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g" #{dir}/**/*.rb" end end Spec Fails!
  • 156. 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 -E flag back in
  • 157. 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!
  • 158. 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 -i flag: sed alters files in place
  • 159. 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 -i flag: sed alters files in place Passing an empty extension
  • 160. First Do No Harm: Surgical Refactoring @nellshamrell spec/do_system_things_spec.rb context ‘changing the file’ do it ‘does not save a backup copy of the file’ do expect (Dir[“#{directory_name}/#{subdirectory_name}/*”] .count).to eq(1) end end Expect only one file in directory/subdirectory path
  • 161. First Do No Harm: Surgical Refactoring @nellshamrell spec/do_system_things_spec.rb context ‘changing the file’ do it ‘does not save a backup copy of the file’ do expect (Dir[“#{directory_name}/#{subdirectory_name}/*”] .count).to eq(1) do_system_things.do_the_thing(directory_name) end end Call the method
  • 162. First Do No Harm: Surgical Refactoring @nellshamrell spec/do_system_things_spec.rb context ‘changing the file’ do it ‘does not save a backup copy of the file’ do 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 Expect only one file in directory/subdirectory path
  • 163. First Do No Harm: Surgical Refactoring @nellshamrell spec/do_system_things_spec.rb context ‘changing the file’ do it ‘does not save a backup copy of the file’ do 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!
  • 164. First Do No Harm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def do_the_thing(dir) system "sed -E -i .tmp ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g" #{dir}/**/*.rb" end end Pass extension to -i flag
  • 165. First Do No Harm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def do_the_thing(dir) system "sed -E -i .tmp ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g" #{dir}/**/*.rb" end end Spec Fails!
  • 166. 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 Passing an empty extension again
  • 167. 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!
  • 168. 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!
  • 169. Section V: Operation First Do No Harm: Surgical Refactoring @nellshamrell
  • 170. 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
  • 171. 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
  • 172. 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
  • 173. 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
  • 174. 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 Spec Passes!
  • 175. 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
  • 176. 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
  • 177. 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 Spec Fails!
  • 178. 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
  • 179. 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 Spec Passes!
  • 180. Section VI: Recovery First Do No Harm: Surgical Refactoring @nellshamrell
  • 181. What do I need to do after refactoring? First Do No Harm: Surgical Refactoring @nellshamrell Need to know two things decisively
  • 182. 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?
  • 183. 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?
  • 184. What do I need to do after refactoring? First Do No Harm: Surgical Refactoring @nellshamrell What about QA?
  • 185. What do I need to do after refactoring? First Do No Harm: Surgical Refactoring @nellshamrell Ideally, QA should find nothing
  • 186. What if something goes wrong? First Do No Harm: Surgical Refactoring @nellshamrell
  • 187. What if something goes wrong? First Do No Harm: Surgical Refactoring @nellshamrell 1) Take responsibility - what your code does in production is your responsibility
  • 188. 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)
  • 189. 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!)
  • 190. Thank You! First Do No Harm: Surgical Refactoring @nellshamrell Nell Shamrell-Harrington Software Development Engineer at Chef @nellshamrell www.nellshamrell.com nshamrell@chef.io
  • 191. Thank You! First Do No Harm: Surgical Refactoring @nellshamrell Nell Shamrell-Harrington Software Development Engineer at Chef @nellshamrell www.nellshamrell.com nshamrell@chef.io Thank you Jim and Jen!!!!