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 method 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 alnum 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…
(Adding in tests for rest of characters)
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
class DoSystemThings
def first_regex_match(string)
/:([[:alnum:]]+)[[:space]]=>/.match(string)
end
def do_the_thing(dir)
system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/1:/g'
#{dir}/**/*.rb"
end
end We know what this regex does!
‘:ab =>’ matches!
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
(note the space and hash rocket)
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 -E -i '' ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g"
#{dir}/**/*.rb"
end
end
Next step…add in tests for these flags
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
class DoSystemThings
def do_the_thing(dir)
# taking the -E flag out
system "sed -i '' ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g"
#{dir}/**/*.rb"
end
end
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
class DoSystemThings
def do_the_thing(dir)
# taking the -E flag out
system "sed -i '' ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g"
#{dir}/**/*.rb"
end
end
Spec Fails!
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
class DoSystemThings
def do_the_thing(dir)
# putting the -E flag back in
system "sed E —i '' ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g"
#{dir}/**/*.rb"
end
end
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
class DoSystemThings
def do_the_thing(dir)
# putting the -E flag back in
system "sed E —i '' ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g"
#{dir}/**/*.rb"
end
end
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
class DoSystemThings
def do_the_thing(dir)
# putting the -E flag back in
system "sed E —i '' ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g"
#{dir}/**/*.rb"
end
end
Spec Passes!
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
spec/do_system_things_spec.rb
context ‘changing the file’ do
it ‘does not save a backup copy of the file’ do
# Expect the subdirectory we created to contain only one file
expect(Dir[“#{directory_name}/#{subdirectory_name}/*”]
.count).to eq(1)
end
end
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 the subdirectory we created to contain only one file
expect(Dir[“#{directory_name}/#{subdirectory_name}/*”]
.count).to eq(1)
do_system_things.do_the_thing(directory_name)
end
end
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 the subdirectory we created to contain only one file
expect(Dir[“#{directory_name}/#{subdirectory_name}/*”]
.count).to eq(1)
do_system_things.do_the_thing(directory_name)
expect(Dir[“#{directory_name}/#{subdirectory_name}/*”]
.count).to eq(1)
end
end
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 the subdirectory we created to contain only one file
expect(Dir[“#{directory_name}/#{subdirectory_name}/*”]
.count).to eq(1)
do_system_things.do_the_thing(directory_name)
expect(Dir[“#{directory_name}/#{subdirectory_name}/*”]
.count).to eq(1)
end
end
Spec Passes!
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
class DoSystemThings
def do_the_thing(dir)
# adding an extension to the -i flag
system "sed E —i .tmp ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g"
#{dir}/**/*.rb"
end
end
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
class DoSystemThings
def do_the_thing(dir)
# adding an extension to the -i flag
system "sed E —i .tmp ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g"
#{dir}/**/*.rb”
end
end
Spec Fails!
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
class DoSystemThings
def do_the_thing(dir)
# removing extension to -i flag
system "sed E —i ‘’ ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g"
#{dir}/**/*.rb"
end
end
First Do No Harm: Surgical Refactoring @nellshamrell
lib/do_system_things.rb
class DoSystemThings
def do_the_thing(dir)
# removing extension to -i flag
system "sed E —i ‘’ ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g"
#{dir}/**/*.rb"
end
end
Spec Passes!
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
Specs Pass!
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 Specs Fail!
w != [:alnum]
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
Specs Pass!
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!)
Conclusion
First Do No Harm: Surgical Refactoring @nellshamrell
“I’m not a great programmer, I’m just a good
programmer with great habits”
- “Refactoring: Ruby Edition”
Thank You!
First Do No Harm: Surgical Refactoring @nellshamrell
Nell Shamrell-Harrington
Software Development Engineer at Chef
@nellshamrell
www.nellshamrell.com
nshamrell@chef.io

First Do No Harm: Surgical Refactoring (extended edition)

  • 1.
    First Do NoHarm Surgical Refactoring Nell Shamrell-Harrington @nellshamrell
  • 2.
    Section I: Refactoring First DoNo Harm: Surgical Refactoring @nellshamrell
  • 3.
    First Do NoHarm: Surgical Refactoring @nellshamrell Refactoring is a change and changes can go wrong What is refactoring?
  • 4.
    But…it’s not lifeor death, right? First Do No Harm: Surgical Refactoring @nellshamrell
  • 5.
    But…it’s not lifeor death, right? First Do No Harm: Surgical Refactoring @nellshamrell Software is being integrated into…
  • 6.
    But…it’s not lifeor death, right? First Do No Harm: Surgical Refactoring @nellshamrell Software is being integrated into… Transportation
  • 7.
    But…it’s not lifeor death, right? First Do No Harm: Surgical Refactoring @nellshamrell Software is being integrated into… Transportation Energy sources
  • 8.
    But…it’s not lifeor 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 Irefactor? First Do No Harm: Surgical Refactoring @nellshamrell 2 Common Approaches
  • 13.
    How should Irefactor? First Do No Harm: Surgical Refactoring @nellshamrell 2 Common Approaches 1) Edit and Pray
  • 14.
    How should Irefactor? First Do No Harm: Surgical Refactoring @nellshamrell 2 Common Approaches 1) Edit and Pray 2) Cover and Modify
  • 15.
    How should Irefactor? 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 FirstDo No Harm: Surgical Refactoring @nellshamrell
  • 17.
    What is surgicalrefactoring? First Do No Harm: Surgical Refactoring @nellshamrell Change exactly what we INTEND to change And ONLY what we intend to change
  • 18.
    What is surgicalrefactoring? First Do No Harm: Surgical Refactoring @nellshamrell First do no harm!
  • 19.
    First Do NoHarm: Surgical Refactoring @nellshamrell Surgical refactoring is a series of good habits that reduce risk What is surgical refactoring?
  • 20.
    First Do NoHarm: Surgical Refactoring @nellshamrell 2 categories of refactoring What is surgical refactoring?
  • 21.
    First Do NoHarm: Surgical Refactoring @nellshamrell 2 categories of refactoring What is surgical refactoring? 1) Necessary refactoring
  • 22.
    First Do NoHarm: Surgical Refactoring @nellshamrell 2 categories of refactoring What is surgical refactoring? 1) Necessary refactoring 2) Cosmetic refactoring
  • 23.
    What is anecessary refactoring? First Do No Harm: Surgical Refactoring @nellshamrell Need to add something
  • 24.
    What is anecessary refactoring? First Do No Harm: Surgical Refactoring @nellshamrell Need to add something Code is too inefficient
  • 25.
    What is anecessary 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 anecessary refactoring? First Do No Harm: Surgical Refactoring @nellshamrell Necessary refactorings have a moderate to high risk tolerance
  • 27.
    What is acosmetic refactoring? First Do No Harm: Surgical Refactoring @nellshamrell
  • 28.
    What is acosmetic refactoring? First Do No Harm: Surgical Refactoring @nellshamrell No business need to change code
  • 29.
    What is acosmetic refactoring? First Do No Harm: Surgical Refactoring @nellshamrell Something about it just bugs us No business need to change code
  • 30.
    What is acosmetic refactoring? First Do No Harm: Surgical Refactoring @nellshamrell Cosmetic refactorings have a low risk tolerance
  • 31.
    What about whitespacerefactoring? First Do No Harm: Surgical Refactoring @nellshamrell
  • 32.
    What about whitespacerefactoring? First Do No Harm: Surgical Refactoring @nellshamrell Ultimate cosmetic refactoring
  • 33.
    What about whitespacerefactoring? First Do No Harm: Surgical Refactoring @nellshamrell Ultimate cosmetic refactoring Get a style guide (i.e. Github style guide)
  • 34.
    What about whitespacerefactoring? 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 insurgical refactoring? First Do No Harm: Surgical Refactoring @nellshamrell
  • 36.
    What’s involved insurgical refactoring? First Do No Harm: Surgical Refactoring @nellshamrell 1) Pre-op: what to do before touching the code
  • 37.
    What’s involved insurgical 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 insurgical 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 NoHarm: 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 DoNo Harm: Surgical Refactoring @nellshamrell
  • 41.
    What is involvedin pre-op? First Do No Harm: Surgical Refactoring @nellshamrell Diagnosis (What exactly does the code do?)
  • 42.
    First Do NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 knowwhat it does? First Do No Harm: Surgical Refactoring @nellshamrell
  • 48.
    So…do we knowwhat it does? First Do No Harm: Surgical Refactoring @nellshamrell Map is influenced by our own experiences and expectations
  • 49.
    So…do we knowwhat 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 knowwhat it does? First Do No Harm: Surgical Refactoring @nellshamrell Best way to repeatedly execute the code is through automated tests
  • 51.
    First Do NoHarm: 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 NoHarm: Surgical Refactoring @nellshamrell spec/do_system_things_spec.rb let(:do_system_things) { DoSystemThings.new } Instantiate the class
  • 53.
    First Do NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/1:/g' #{dir}/**/*.rb" end end Rdocs: system method returns true when the command executes successfully
  • 65.
    First Do NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb Find match for this pattern s/:([[:alnum:]]+)[[:space:]]=>/1:/g'
  • 89.
    First Do NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb Replace it with this pattern s/:([[:alnum:]]+)[[:space:]]=>/1:/g'
  • 90.
    First Do NoHarm: 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 NoHarm: 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 NoHarm: Surgical Refactoring @nellshamrell spec/do_system_things_spec.rb describe ‘what the regex matches’ # FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/ end
  • 93.
    First Do NoHarm: Surgical Refactoring @nellshamrell spec/do_system_things_spec.rb describe ‘what the regex matches’ # FOR REFERENCE: /:([[:alnum:]]+)[[:space:]]=>/ let(:string) {‘'} end
  • 94.
    First Do NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def first_regex_match(string) /:[[:alnum:]]/.match(string) end def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/1:/g' #{dir}/**/*.rb" end end Will return successful match with only one alnum character
  • 113.
    First Do NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def first_regex_match(string) /:([[:alnum:]]+)[[:space]]=>/.match(string) end def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/1:/g' #{dir}/**/*.rb" end end Fast Forward… (Adding in tests for rest of characters)
  • 128.
    First Do NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def first_regex_match(string) /:([[:alnum:]]+)[[:space]]=>/.match(string) end def do_the_thing(dir) system "sed -E -i '' 's/:([[:alnum:]]+)[[:space:]]=>/1:/g' #{dir}/**/*.rb" end end We know what this regex does! ‘:ab =>’ matches!
  • 129.
    First Do NoHarm: 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 (note the space and hash rocket)
  • 130.
    First Do NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb 1:
  • 138.
    First Do NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb 1: Escape - so we can use a literal as the next character
  • 139.
    First Do NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb 1: Uses the result of the first capture group from the first pattern
  • 140.
    First Do NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb 1: Adds a literal colon
  • 141.
    First Do NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb s/:([[:alnum:]]+)[[:space:]]=>/1:/g' ‘:ab =>’
  • 142.
    First Do NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb s/:([[:alnum:]]+)[[:space:]]=>/1:/g' ‘ab:’‘:ab =>’ replace with
  • 143.
    First Do NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: 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 NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def do_the_thing(dir) system "sed -E -i '' ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g" #{dir}/**/*.rb" end end Next step…add in tests for these flags
  • 155.
    First Do NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def do_the_thing(dir) # taking the -E flag out system "sed -i '' ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g" #{dir}/**/*.rb" end end
  • 156.
    First Do NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def do_the_thing(dir) # taking the -E flag out system "sed -i '' ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g" #{dir}/**/*.rb" end end Spec Fails!
  • 157.
    First Do NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def do_the_thing(dir) # putting the -E flag back in system "sed E —i '' ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g" #{dir}/**/*.rb" end end
  • 158.
    First Do NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def do_the_thing(dir) # putting the -E flag back in system "sed E —i '' ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g" #{dir}/**/*.rb" end end
  • 159.
    First Do NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def do_the_thing(dir) # putting the -E flag back in system "sed E —i '' ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g" #{dir}/**/*.rb" end end Spec Passes!
  • 160.
    First Do NoHarm: 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
  • 161.
    First Do NoHarm: 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 the subdirectory we created to contain only one file expect(Dir[“#{directory_name}/#{subdirectory_name}/*”] .count).to eq(1) end end
  • 162.
    First Do NoHarm: 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 the subdirectory we created to contain only one file expect(Dir[“#{directory_name}/#{subdirectory_name}/*”] .count).to eq(1) do_system_things.do_the_thing(directory_name) end end
  • 163.
    First Do NoHarm: 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 the subdirectory we created to contain only one file expect(Dir[“#{directory_name}/#{subdirectory_name}/*”] .count).to eq(1) do_system_things.do_the_thing(directory_name) expect(Dir[“#{directory_name}/#{subdirectory_name}/*”] .count).to eq(1) end end
  • 164.
    First Do NoHarm: 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 the subdirectory we created to contain only one file expect(Dir[“#{directory_name}/#{subdirectory_name}/*”] .count).to eq(1) do_system_things.do_the_thing(directory_name) expect(Dir[“#{directory_name}/#{subdirectory_name}/*”] .count).to eq(1) end end Spec Passes!
  • 165.
    First Do NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def do_the_thing(dir) # adding an extension to the -i flag system "sed E —i .tmp ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g" #{dir}/**/*.rb" end end
  • 166.
    First Do NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def do_the_thing(dir) # adding an extension to the -i flag system "sed E —i .tmp ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g" #{dir}/**/*.rb” end end Spec Fails!
  • 167.
    First Do NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def do_the_thing(dir) # removing extension to -i flag system "sed E —i ‘’ ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g" #{dir}/**/*.rb" end end
  • 168.
    First Do NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def do_the_thing(dir) # removing extension to -i flag system "sed E —i ‘’ ’s/:([[:alnum:]]+)[[:space:]]=>/1:/g" #{dir}/**/*.rb" end end Spec Passes!
  • 169.
    First Do NoHarm: 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!
  • 170.
    Section V: Operation First DoNo Harm: Surgical Refactoring @nellshamrell
  • 171.
    First Do NoHarm: 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 NoHarm: 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
  • 173.
    First Do NoHarm: 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
  • 174.
    First Do NoHarm: 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
  • 175.
    First Do NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def do_the_thing(dir) system "sed -E -i '' #{substitute_command} #{dir}/**/*.rb" end private def substitute_command 's/:([[:alnum:]]+)[[:space:]]=>/1:/g' end end Specs Pass!
  • 176.
    First Do NoHarm: 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
  • 177.
    First Do NoHarm: 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
  • 178.
    First Do NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def do_the_thing(dir) system "sed -E -i '' #{substitute_command} #{dir}/**/*.rb" end private def substitute_command ’s/:(w+)s=>/1:/g’ end end Specs Fail! w != [:alnum]
  • 179.
    First Do NoHarm: 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
  • 180.
    First Do NoHarm: Surgical Refactoring @nellshamrell lib/do_system_things.rb class DoSystemThings def do_the_thing(dir) system "sed -E -i '' #{substitute_command} #{dir}/**/*.rb" end private def substitute_command 's/:([[:alnum:]]+)[[:space:]]=>/1:/g' end end Specs Pass!
  • 181.
    Section VI: Recovery First DoNo Harm: Surgical Refactoring @nellshamrell
  • 182.
    What do Ineed to do after refactoring? First Do No Harm: Surgical Refactoring @nellshamrell Need to know two things decisively
  • 183.
    What do Ineed to do after refactoring? First Do No Harm: Surgical Refactoring @nellshamrell Need to know two things decisively 1) Does the behavior still exist?
  • 184.
    What do Ineed 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?
  • 185.
    What do Ineed to do after refactoring? First Do No Harm: Surgical Refactoring @nellshamrell What about QA?
  • 186.
    What do Ineed to do after refactoring? First Do No Harm: Surgical Refactoring @nellshamrell Ideally, QA should find nothing
  • 187.
    What if somethinggoes wrong? First Do No Harm: Surgical Refactoring @nellshamrell
  • 188.
    What if somethinggoes wrong? First Do No Harm: Surgical Refactoring @nellshamrell 1) Take responsibility - what your code does in production is your responsibility
  • 189.
    What if somethinggoes 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)
  • 190.
    What if somethinggoes 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!)
  • 191.
    Conclusion First Do NoHarm: Surgical Refactoring @nellshamrell “I’m not a great programmer, I’m just a good programmer with great habits” - “Refactoring: Ruby Edition”
  • 192.
    Thank You! First DoNo Harm: Surgical Refactoring @nellshamrell Nell Shamrell-Harrington Software Development Engineer at Chef @nellshamrell www.nellshamrell.com nshamrell@chef.io