Intro to Software
Engineering
ADA University
School of IT and
Engineering
1
Intro to Software Engineering
Fall 2023
Construction
Readability
▪ Non-structural properties we want to control in our systems
• complexity
• performance
• security
• usability
• portability
• scalability
▪ Readability
• easier for others to understand our code
• easier to make changes
• reduces chance of introducing bugs
▪ We really want to avoid being frustrated by an opaque bit of
code after realizing that we wrote it 6 months ago
3
Readability
▪ Readable code acts as documentation
National
Research
Council of
Canada
▪ Documentation can become outdated
▪ Readable code is the ultimate spec of what the system is doing
▪ Readability anti-patterns to follow…
• common solutions to common problems where the solution is
ineffective and may result in undesired consequences
4
Anti-pattern: deep nesting
▪ 6 levels deep
▪ hard to understand what is
going on
▪ after 5 levels deep, a
return statement!? what is
it about figure being Mario
that it worth NOT running
the rest of the code?
5
Anti-pattern: deep nesting
▪ setting time to 0 and
calling play
• 6 levels deep inside the
constructor
• inside an anonymous
callback
▪ challenging to
understand why this code
is here by just looking at
this code alone
▪ if this code was inside a
method with a
reasonable name, then
we would have
understood better
6
Anti-pattern: bad naming
▪ figure.q2q what is it?
▪ no useful information from
the name
▪ after reading the whole
code, we understand that it
returns false if figures are
far away from each other
▪ BUT, we shouldn’t be in this
situation
7
Anti-pattern: bad naming
▪ death() method has lots of
variables which starts with
death
▪ if they were declared with
different names, then it
would have been
understandable
8
Anti-pattern: individual style and no comments
▪ Consistent style makes it easier for new people to make
modifications
▪ lack of comments = no documentation within the code about
types and methods
▪ Also, a goal for readable code is we want the code to read like
natural language
▪ Example, star code:
9
Anti-pattern: others
▪ This code would definitely benefit
from being broken down into
more digestible pieces, like
paragraphs in a book
▪ Another thing we want to do is,
we want to write our code in a
way that explains itself
▪ Moreover, we want our code to
tell a story
10
Static Analysis
▪ Annotations in IDEs (Visual Studio, Eclipse, etc.) come from compilers
▪ Also tells about other errors
▪ Compilers ingest source code and output errors
▪ Compilers don’t give any semantic warnings, they only validate syntax
▪ Linters are class of static analysis tools
▪ They provide semantic warnings to help avoid common mistakes (they do
this by applying variety of heuristics)
▪ Linters can also ensure that code adheres to a team’s style guide (i.e., by
checking the indentation and how braces are used)
11
Lint examples
12
Lint examples
Note: Linters also catch simple problems, not always detrimental issues
13
Automation
▪ Automation is crucial to modern software
• team size
• complexity
• pace of development
▪ Allows building software in the following ways
• reliable
• repeatable
• revertible
14
Automation
15
Automation
manual task
automated task
16
Automation
17
Automation
18
Automation
19
Automation
20
Automation
21
Automation
22
Automation
▪ Repeatable
• enable systems to be built by various automated systems
▪ Reliable
• deterministically perform repeatable actions while getting the same
results
• fundamental for stakeholders’ trust
▪ Revertible
• decrease the time in case failure occurs
23
Software Maintenance
▪ Majority of software development is performed on existing
systems
▪ Maintenance & evolution are the primary components of
the system’s total budget
▪ Software changes are
• Corrective (21%)
◦ fix defects
• Adaptive (25%)
◦ help the system work to interoperate with external changes
• Perfective (50%)
◦ add or modify functionality
• Preventative (4%)
◦ improve program structure
24
Software Maintenance
▪ Software maintenance is hard
▪ Code can become complex and unstructured
• sometimes due to poor initial design
▪ Commonly due to a lack of preventative maintenance
▪ Also, when software engineers lack sufficient domain or
historical knowledge about their projects
▪ Time pressures lead to quick fixes and degrade quality
25
Code Smells
▪ As code evolves, fixing bugs and adding features becomes
harder
▪ Code smells are negative attributes that long-lived systems
exhibit
▪ Martin Fowler defined 5 types
• bloaters
• object-oriented abusers
• change preventers
• dispensables
• couplers
26
Bloaters
▪ Size of program elements make them hard to
understand/change
▪ Examples are
• long method
• large class
• long parameter lists
• primitive obsession
• data clump
27
Treatment: long method
▪ Move this code to a separate new method (or function) and
replace the old code with a call to the method
▪ If local variables and parameters interfere with extracting a
method, (a) replace temp with query, (b) introduce a
parameter object or (c) preserve whole object
▪ If none of the previous recipes help, try moving the entire
method to a separate object
28
Treatment: large class
▪ Extract class helps if part of the behavior of the large class
can be spun off into a separate component
▪ Extract subclass helps if part of the behavior of the large
class can be implemented in different ways or is used in rare
cases
▪ Extract interface helps if it is necessary to have a list of the
operations and behaviors that the client can use.
29
Treatment: long parameter lists
▪ If some of the arguments are just results of method calls of
another object, replace parameter with method call. This
object can be placed in the field of its own class or passed as
a method parameter.
▪ Instead of passing a group of data received from another
object as parameters, pass the object itself to the method
▪ If there are several unrelated data elements, sometimes you
can merge them into a single parameter object
30
Treatment: primitive obsession
▪ If you have a large variety of primitive fields, it may be
possible to logically group some of them into their own
class. Even better, move the behavior associated with this
data into the class too
▪ If the values of primitive fields are used in method
parameters, introduce a parameter object
▪ When complicated data is coded in variables, replace type
code with class, subclass and/or Strategy (or State) pattern
▪ If there are arrays among the variables, replace the array
with an object
31
Object-Oriented Abusers
▪ OO abusers are a class of smells that arise when objectoriented design is not leveraged
▪ Examples are
• switch statements
• temporary field
• refused bequest
• alternative classes with different interfaces
32
Treatment: switch statements
▪ You may need to extract/move method to isolate switch and
put it in the right class
▪ If a switch is based on type code, replace type code with
subclasses and/or Strategy (or State) pattern
▪ After specifying the inheritance
structure, replace conditional with
polymorphism
▪ If one of the conditional options is null, introduce Null
Object
33
Treatment: temporary field
▪ Temporary fields and all code operating on them can be put
in a separate class. In other words, you are creating a
method object, achieving the same result as if you would
replace method with method object
▪ Introduce Null Object and integrate it in place of the
conditional code which was used to check the temporary
field values for existence
34
Treatment: refused bequest
▪ This comes from violation of the Liskov
principle
▪ If inheritance makes no sense and the
subclass really does have nothing in
common with the superclass, eliminate
inheritance in favor of delegation
▪ If inheritance is appropriate, get rid of
unneeded fields and methods in the
subclass. Extract all fields and methods
needed by the subclass from the parent
class, put them in a new subclass, and
set both classes to inherit from it
35
Treatment: alternative classes with different interfaces
▪ Two classes perform identical functions but have different method
names
▪ Rename methods to make them identical in all alternative classes
▪ Make the signature and implementation of methods the same
▪ If only part of the functionality of the classes is duplicated, try
extracting superclass. In this case, the existing classes will become
subclasses.
▪ After you have determined which treatment method to use and
implemented it, you may be able to delete one of the classes
36
Refactoring and Technical Debt
▪ Designs degrade as software evolves
▪ Developers often find fixes that ‘work’
• They don’t understand original design
• They may have time pressure
▪ Technical debt refers to the cost of these fixes
• Often needs to be repaid in the future ☺
• If not repaid, overall system viscosity increases
▪ The idea of thinking about technical debt is to encourage
long-range planning
37
Technical Debt
Technical Debt
Reckless
Prudent
“Our deadline is
“We don’t have time
Deliberate
firm, but the risk is
to think about it”
worth it”
“It will work in
“We were lucky, but
Inadvertent production without next time we can do
any testing”
better”
▪ Martin Fowler described it as a quadrant
▪ Vertical dimension: intentional or accidental
▪ Horizontal dimension: justification thought or not
38
Technical Debt Terminology
Technical Debt
Reckless
Prudent
Deliberate
Irresponsible
Intentional
Inadvertent
Incompetent
Accidental
39
Refactoring and Technical Debt
▪ Refactoring is one of the most common ways to pay down
technical debt
• Improve the design while preserving the semantics
• Examples: treatment of the code smells, reduce coupling, increase
cohesion, improve reusability
40
Rule of Threes
▪ When a new feature is needed to be added:
1. Code the feature
▪ The second time the same or extremely similar feature is
needed:
2. Code it, but take note
▪ The third time:
3. Refactor code first
▪ Premature refactoring is a problem
▪ Don’t want to spend all our time refactoring
▪ Sometimes we need to focus on adding new features and
fixing bugs
41
Refactoring Steps
1. Understand the code
2. Transform the code
•
Mechanical steps, often assisted by an IDE
3. Refine the code
•
Rough edges caused by the transformation
▪
Program behavior should be unchanged
▪
Test suite should run before and after change
▪
Without effective tests, it is harder to refactor
42
Refactoring
▪
Often triggered by quality concern
▪
Code review can also indicate need to refactor
▪
Technical team benefits, but risky and costly for other
stakeholders
▪
Refactoring earlier is easier than later
▪
Integrates well with Agile methodologies
▪
Most refactoring involves restructuring classes and
methods
P.S. Great resource about refactoring - 99 Bottles of OOP by Sandi Metz
43