SlideShare a Scribd company logo
1 of 71
MAINTAINING
YOUR CODE
CLINT
EASTWOOD
STYLE
REBECCA WIRFS-BROCK
REBECCA@WIRFS-BROCK.COM
@REBECCAWB
“Whatever
success I've
had is due to a
lot of instinct
and a little
luck.”
—Clint Eastwood
“Whatever
success I’ve
had is through
a lot of hard
work and a
little luck.”
—Rebecca Wirfs-Brock
THE
GOOD
“I like the notion of
working the
program, like an
artist works a lump
of clay..”
—Ward Cunningham
Subscription Quantity Interface:
Version 1
public interface SubscriptionQuantity {
public Integer getQuantity();
public String getUnits();
public DeliveryBasis getDeliveryBasis();
public String toString();
}
Abstract
class
Interface
Subscription Quantity Implementation:
Version 1
@Override
public TimeBasedQuantity add(TimeBasedQuantity amount) {
return new MonthQuantity(getQuantity() + amount.asMonths().getQuantity());
}
@Override
public TimeBasedQuantity add(TimeBasedQuantity amount) {
if (amount.getUnits() == getUnits()) {return new YearQuantity(getQuantity() + amount.getQuantity());}
else {return new MonthQuantity(asMonths().getQuantity() + amount.getQuantity());}
}
Adding to a Year returns a new Month Quantity unless units are Years
Adding to a Month Quantity returns a new Month Quantity
public abstract TimeBasedQuantity add(TimeBasedQuantity amount);
Time Based Quantity declares add must be implemented by subclasses
Subscription Quantity Implementation:
Version 1
public SubscriptionQuantity add(SubscriptionQuantity amount) {
if (amount.getDeliveryBasis() != getDeliveryBasis())
{throw new IllegalArgumentException("Cannot subtract issues from time");}
else return (add((TimeBasedQuantity) amount));
}
public TimeBasedQuantity add(TimeBasedQuantity amount) {
return new MonthQuantity(getQuantity() + amount.asMonths().getQuantity());
}
Adding to a Year returns a new Month Quantity unless units are Years
Adding to a Time Based Quantity
public TimeBasedQuantity add(TimeBasedQuantity amount) {
if (amount.getUnits() == getUnits()) {
return new YearQuantity(getQuantity() + amount.getQuantity());}
else {return new MonthQuantity(asMonths().getQuantity() + amount.getQuantity());}
}
Adding to a Month Quantity returns a new Month Quantity
The Current Definition
public interface SubscriptionQuantity {
public abstract Integer getQuantity();
public abstract String getUnits();
public abstract DeliveryBasis getDeliveryBasis();
public abstract String toString();
public abstract SubscriptionQuantity multiplyBy(int amount);
public abstract SubscriptionQuantity multiplyBy(SubscriptionQuantity amount);
public abstract SubscriptionQuantity subtract(int amount);
public abstract SubscriptionQuantity subtract(SubscriptionQuantity amount);
public abstract SubscriptionQuantity add(int amount);
public abstract SubscriptionQuantity add(SubscriptionQuantity amount);
public abstract boolean lessThan(SubscriptionQuantity amount);
public abstract boolean greaterThan(SubscriptionQuantity amount);
}
“So today, let's write a
program simply. But let's
also realize that
tomorrow, we're going to
make it more
complex, because
tomorrow it's going to
do more”
—Ward Cunningham
A Story of Consistent Error Reporting
Evolution
• Sprint 1: Ad hoc error detection in domain entities and
services
– Sometimes setters threw exceptions
– Sometimes they did not check for anything (fail fast approach)
– This was sprint 1 after all!
• Sprint 2: Some design discipline, but maybe not all good
ideas
– Well-formed domain object constructors don’t allow invalid
state
– Attribute setter methods throw exceptions
– Services catch errors and do their best to report them
– Problem: Sometimes objects possibly can get into inconsistent
states
– Problem: Sometimes not all errors are detected (bailing early)
Ongoing Evolution
• Sprint 3
– ErrorReporter
– ErrorReportingService abstraction
– All services designed to collect and report all errors detected
during handling of a request
• Sprint 4:
– User error messages declared in an external resource
– Ability to add parameter values into messages to improve
logging and support localization
• Coming soon???
– ErrorValidator classes (check for cross-attribute constraints)
public class ErrorReportingService implements ErrorReporter {
private MessageHolder errorMessages = new MessageHolder();
public boolean hasErrors() {return (errorMessages.size() > 0);}
public boolean hasErrorMessage(String errorKey) {
return errorMessages.containsKey(errorKey);}
public void addErrorMessage(String key, String message) {
errorMessages.addMessage(key, message);}
public String getErrorMessage(String errorKey) {
return errorMessages.getMessage(errorKey);}
public Set<String> getErrorMessageKeys() {
return errorMessages.keySet();}
/**
* If any errors have been accumulated, throws MultipleMessageException holding
* the corresponding error messages.
* @throws MultipleMessageException
*/
protected void handleErrors() throws MultipleMessageException {
if (hasErrors()) {MultipleMessageException exception =
loadMultipleMessageException(new MultipleMessageException());
throw exception;}
}
/**
* @param loadable - exception into which this service's error messages are to be copied.
* @return MultipleMessageException holding the same error messages as this service.
*/
protected MultipleMessageException loadMultipleMessageException(MultipleMessageException loadable) {
for (String nextKey : getErrorMessageKeys()) {
loadable.addErrorMessage(nextKey, getErrorMessage(nextKey));}
return loadable;
}
ErrorReporting
Service
public Account createAccount(
User user,
String customerName,
PaymentType paymentType,
CheckPaymentDetails preferredCheckingDetails,
CreditCardPaymentDetails preferredCreditCardDetails,
Address shippingAddress,
Address billingAddress) throws MultipleMessageException
{
// cross-field validation.
verifyRequiredParameters(user,paymentType,preferredCheckingDetails,preferredCreditCardDetails,shippingAddress,billingAddress);
handleErrors();
Account account = new Account(customerName);
account.setUser(user);
account.setPreferedPaymentMethod(paymentType);
account.setCheckPaymentInfo(preferredCheckingDetails);
account.setCheckPaymentInfo(preferredCreditCardDetails);
account.setShippingAddress(shippingAddress);
account.setBillingAddress(billingAddress);
handleErrors();
// Persist.
try {account = getRepository().add(account);
} catch (AccountAlreadyExistsException accountAlreadyExistsException) {
addErrorMessage(ACCOUNT_ALREADY_EXISTS_ERROR, accountAlreadyExistsException.getMessage());
}
handleErrors();
return account;
}
Creating an Account
“Sometimes if you
want to see a change
for the better, you
have to take things
into your own
hands.”
—Clint Eastwood
The Broken
Window Theory
“If the windows
are not
repaired, the
tendency is for
vandals to break a
few more
windows.”
— James Q. Wilson &
George L. Kelling
Small Irritations Corrected
@Test
(expected = IllegalArgumentException.class)
public void testEmailAddressMustIncludeAtSign(){
String username = "rebeccawb";
String password = "abAB123~";
String emailAddress = "rebecca.wirfs-brock.com";
new User(username, password, emailAddress);
}
@Test
public void testEmailAddressMustIncludeAtSign(){
String username = "rebeccawb";
String password = "abAB123~";
String emailAddress = "rebecca.wirfs-brock.com";
expect(IllegalArgumentException.class);
new User(username, password, emailAddress);
}
“Fixed warnings about unused variables by using JUnit 4 annotations for expected
exceptions and by removing variables that are never used, because exception is expected
in constructor.”
A Rhythm of
Restructuring, Refactoring and
Ongoing CleanupDate: April 15, 2013 11:35:46 PM PDT
Refactored package structure to group classes
into packages organized by layer of the
architecture: domain, service, repository.
Date: April 17, 2013 8:25:51 PM PDT
Made class variables private
Date: April 20, 2013 3:19:39 PM PDT
Implemented AccountRepository. Refactored
test data, so that it can be shared between
tests. Cleaned up UserRepositoryStub.
Changed comments in Account to Javadoc
comments.
Date: April 18, 2013 12:31:18 AM PDT
Refactor error messages in Entity objects by
using new MessageHandler class and
ErrorReporter interface. Specify
AccountService interface to create Account.
Date: April 22, 2013 2:47:35 PM PDT
Removed print statements from tests.
Date: April 22, 2013 9:56:10 AM PDT
Fixed password encryption by using a fixed salt
generator and a digester class instead of using
the standard password encryptors.
In a production system we would use more
complicating salting algorithms, but this
demonstrates wrapping a library in a service
“Good code is its
own best
documentation.”
—Steve McConnell
Well maybe….
public void setPassword(String newPassword) {
if (newPassword == null) {throw new IllegalArgumentException("The password may not be set to null.");}
newPassword = newPassword.trim();
if (newPassword.matches("[a-zA-Z]{1}")) {
throw new IllegalArgumentException("The password must contain at least one alpha character.");}
if (!newPassword.matches("^.*d.+$")) {
throw new IllegalArgumentException("The password must contain at least one digit.");}
if (newPassword.length() < 6 || newPassword.length() > 32){
throw new IllegalArgumentException("The password must be 6 - 32 character and contain at least one
letter and one number.");}
if (newPassword.matches("^.*s.*$")) {
throw new IllegalArgumentException("The password must not contain space or non-printing
characters.");}
this.password = newPassword;
}
My exception messages documented the rules.
Without them, I’d be lost.
Regex expressions are not documentation, no matter how straightforward
private void addBuildingNameFloorRoomNumberLine() {
String buildingName = address.getBuildingName();
String floor = address.getFloor();
String room = address.getRoomNumber();
Boolean buildingNameExists, floorExists, roomExists = false;
buildingNameExists = buildingName.length() != 0;
if (buildingNameExists){
builder.append(buildingName); buildingNameExists = true;
}
floorExists = floor.length() !=0;
if (floorExists) {
if (buildingNameExists) {builder.append(", ");}
builder.append(floor);
}
roomExists = room.length() !=0;
if (roomExists) {
if (floorExists) { builder.append(", ");}
else if (buildingNameExists) {builder.append(", ");}
builder.append(room);
}
if (buildingNameExists || floorExists || roomExists) builder.append("n");
}
OK….
Some “good” code is
just tedious.
But you can do
things to make it
more legible.
Good code is not
“beautiful code”.
Do not confuse
goodness with
beauty.
“I tried being
reasonable. I
didn’t like it.”
—Clint Eastwood as
Dirty Harry
Do Style Guides
Help?
“Go ahead, make my day.”
—Clint Eastwoodin Sudden Impact
http://source.android.com/source/code-style.html
“Respect your
efforts, respect
yourself. Self-
respect leads to
self-discipline.”
—Clint Eastwood
Import Statements
Style over Substance?
• Order:
– Android imports first
– Then third parties (com, junit, net, org)
– Finally, java and avax
• Formatting:
– Alphabetical within each grouping, with capital letters
before lower case letters (e.g. Z before a).
– A blank line between each major grouping (android,
com, junit, net, org, java, javax).
• The fine print:
– Use explicit class names instead of *
Naming Conventions
Style over Substance?
The Rules
• Start non-public, non-static field
names with m.
• Start static field names with s.
• Start all other fields with a lower
case letter
• Public static final fields
ALL_CAPS_WITH_UNDERSCORES
public class MyClass {
public static final int SOME_CONSTANT = 42;
public int publicField;
private static MyClass sSingleton;
int mPackagePrivate;
private int mPrivate;
protected int mProtected;
}
Field Definitions
Do options lead to inconsistency?
• Define fields in standard places
• Fields should be defined at the top of the file
• Or before methods that use them
OK, which is preferred?
Exception Handling Guidelines
Do options lead to inconsistency?
• Prime Directive: Never swallow an exception
• Don’t catch Exception. Instead catch specific
exceptions by name
• Prioritized list of handling strategies:
1. Throw a new exception that matches your level of
abstraction
2. Handle gracefully and when possible substitute an
appropriate default
3. Catch and then rethrow a runtime exception
(because your app should crash)
4. If you ignore an exception, say why in a comment
“I get lazy or
bored, even with good
intentions it’s hard to
be consistent
programming.
It’s easier to be a
consistent runner.”
—Rebecca Wirfs-Brock
“Our parting thought: BE CONSISTENT. If you're
editing code, take a few minutes to look at the
code around you and determine its style.”
—The Google App Style Guideline Authors
“But local style is also important. If code you add
to a file looks drastically different from the
existing code around it, it throws readers out of
their rhythm when they go to read it. Try to
avoid this.” —The Google App Style Guideline Authors
THE
BAD
HOW DO YOU LIVE SOMEONE
ELSE’S BAD CODE?
“My first impression was confusion.
Some of the code is procedural, some is object-
oriented, some global variables are used, and the
database is a forest of related tables….
I’ve learned to read and write Moodle, to modify
it and expand it with custom plugins, to debug it
and figure out what is going on even when the log
files aren’t helping at all.”
-Olja Petrovic
The Apprentice Coder’s Blog
Living inside Moodle e-Learning
Platform, source code, and DB
Making Sense of “Mess-View-Controller”
• This isn’t Model-View-
Controller
• Logic is mixed with
presentation in absurd
ways
• No one else thinks this
is a problem
What’s already there isn’t going to get fixed.
Technically, it isn’t broken.
Just bad or awkward or error prone or unreliable or not the way you’d do it
“Norms weren’t established.
Conventions (if any) grow organically.”
“Not what I’m used to”
Knowing the code isn’t enough…
• You’ve got to crack how the database works
and is updated.
• It ties everything together
– Users and course activities and events
– The platform itself – the plugins and blocks and
bits and pieces and roles and contexts
schemaball
Martin Langhoff
A Recipe for Making Changes
1. Figure out how a similar
activity is done
2. Then:
– Write a plugin (enrol or auth)
– Add a question type
– Add a custom user profile field
type
– Override a class or function
– Hack the core
• Safe/easy
• Requires deeper knowledge
• Done at your own risk
HOW DO YOU LIVE WITH YOUR
OWN BAD CODE?
“When inexperienced developers write bad
code, their code is just bad. It has few (if any)
redeeming qualities. It often must be
scrapped, or at a minimum refactored
intensely.”
-Brandon Savage
Conventional Wisdom
Refactoring Experience Report
Amr Noaman, Agile 2013
• Initial, approach with three teams:
– First, write system/functional automated tests to enable safe
refactorings
– Then identify design patterns or any other best practice, and
transform the code
– Keep the codebase working and add features/fix bugs
• Results: Failure, abandoned efforts, disappointment, frustration
• Observations:
– Refactoring objectives too vague
– Automated tests were difficult to impossible to write for bad code
– Poor planning and measurement
– Went too deep with major refactorings without finishing
– Lack of management support
– Communication problems
– Unsustainable development
Refactoring Experience Part 2:
A more systematic approach
• Prepare code to be increasingly refactorable
through a simple, continuous, and sustainable
set of refactorings
Measure Quick-Win Progress
Metric Description Unit of Measure Target/ Threshold
Code size Number of lines of
code excluding
comments and white
space.
KLOC Reaching a plateau
for 2 or more
iterations.
Code Size Reduction
Speed (CSRS)
Number of lines of
code removed per one
hour of refactoring.
LOC This metric should be
stable. Ones it drops,
it is an indicator to
stop this type of
refactoring
Number of duplicated
code lines
Calculated by
counting absolute
number of lines
which are part of at
least one (10 LOC or
more) duplicate.
Absolute Reaching a plateau
for 2 or more
iterations.
Note that not all
duplicates can be
removed.
Refactoring “Rules”
• Refactor on the main branch
• Limit scope at first:
– do quick win refactorings only,
– then restructure and write automated component
tests
• Time allocated to refactoring specified by the
senior manager as a % of team effort for each
iteration.
Measure Examples
Removing dead code cost one team 36 hours,
and reduced the code size by 6.4%.
46% of their code was duplicated!
Refactoring for another project was more
challenging due to complex and financially
critical problem domain.
However, they still removed 40 kloc in 113
hours.
New Wisdom:
Improvement is better than inertia
• Small improvements can lead to bigger ones
• Ongoing:
– Spend an hour or two “cleaning up” while freeing your mind to
do “background” problem solving
• Small improvements to do:
– Write a test
– Refactor a test
– Revise a function
– Change some code to work better
– Factor into smaller chunks
– Untangle overly-complicated logic
– Remove dead code
– …
“A good man
always knows his
limitations.”
—Clint Eastwood
as Dirty Harry in Magnum Force
Sustainable Refactoring
• 10 % or less of total time
• On the main branch
• With management approval/buy in
• With identified success criteria
• Modest first, then more aggressive
• Stop when you get diminished returns
THE
UGLY
At a float altitude of 128,000 feet, the balloon inflates to 40 million cubic feet. Photo Credits: NASA/Nagoya University
Thanks Ben Zeiger for sharing the code and his lab where they are building/testing and preparing for 2014 launch.
Software to Control the NASA X-ray
Telescope InFOCμs
Beautiful Technology, Ugly Code
• The code:
– Originally written in MATLAB
– Translated to C
– “Light” on comments
– Written by electrical
engineer/physics guy, not sw
dev
– Mega-big functions
– Lots of calculations
– The algorithms work
– It’s timing and control code
that has bugs
– Consequences of bugs: not able
to locate accurately, missed
repositioning (leading to
instability)
// ------------------------------------------------------------------------
// TN-4/27/12 New function to resynchronize
// serial interface and return error code.
PMDuint16 ethSerial_Resync(void* transport_data, PMDuint16 errcode)
{
HANDLE_TABLE *p = (HANDLE_TABLE *)transport_data;
PMDuint16 synccode = ethSerial_Sync(transport_data);
/*
unsigned short status, statush, statusd, statusdf, mode;
PMDGetEventStatus(p->axish, &status);
if (status > 1){
char binary[17];
itoa(status, binary, 2);
printf("%f: Host: %s Event Status: %sn", get_current_time(), p-
>hostname, binary);
PMDGetDriveStatus(p->axish, &statusd);
itoa(statusd, binary, 2);
printf("%f: Host: %s Drive Status: %sn", get_current_time(), p-
>hostname, binary);
PMDGetDriveFaultStatus(p->axish, &statusdf);
printf("Host: %s Drive Fault Status: %#Xn", p->hostname, statusdf);
if( statusdf > 0x01){
PMDClearDriveFaultStatus(p->axish);
printf("%f: Drive Fault Status
Clearedn", get_current_time());
PMDResetEventStatus(p->axish, 0);
PMDRestoreOperatingMode(p->axish); }
PMDGetOperatingMode(p->axish, &mode);
itoa(mode, binary, 2);
printf("Host: %s Operating Mode: %sn", p->hostname, binary);
PMDGetInstructionError(p->axish, &statush);
printf("%f: Host: %s Instruction Error: %#Xn", get_current_time(), p-
>hostname, statush );
PMDResetEventStatus(p->axish, 0);
}
*/
//JWM 5/3 Added to diagnose when resync occurs to try to understand why.
printf("Resync: %s Due to: %#X , synccode: %#X @: %f n", p->hostname, errcode, synccode, get_current_time());
format_resync_pkt(p->hostname, errcode, synccode, get_current_time());
void Controller(double tc, VECTOR *qc, VECTOR *wc, double ttag, VECTOR *qhat, VECTOR *what, RWDATA
*RWmeas, LADATA *LAmeas, PVDATA *PVmeas,
VECTOR *tauw, VECTOR *dc, VECTOR *ddc, double
*tauaz, double *wcaz, VECTOR *dcount, LFCMD *LFcmd, RWACMD *RWAcmd)
{
double R_d[9], Rt_d[9], g_eci_d[3], g_b_d[3], dmg_d[3], hw_d[3], hb_d[3];
double H_d[3], He_d[3], Y2_d[3], Z2_d[3], Y1_d[3], tempY2_d[3], d_d[3];
double dm_d[3], tauh_d[3], tempg_b_d[3], temp_what_d[3];
double qe_d[4], ae_d[3], we_d[3], temp_qconj_d[4], u_d[3], a_d[3];
double tau_pid_d[3], taugy_d[3], taugg_d[3], tauc_d[3], taub_d[3], tauwc_d[3], qhatp_d[4];
VECTOR g_ecf = {3, 1, PVmeas->p, 0}; //vector_alloc_from_array(3, PVmeas->p);
MATRIX R = {3, 3, R_d, 0}; //matrix_alloc(3, 3);
MATRIX Rt = {3, 3, Rt_d, 0}; // matrix_alloc(3, 3);
VECTOR g_eci = {3, 1, g_eci_d, 0}; //vector_alloc(3);
VECTOR g_b = {3, 1, g_b_d, 0}; //vector_alloc(3);
VECTOR dmg = {3, 1, dmg_d, 0}; //vector_alloc(3);
MATRIX Wb_w, Ww_b, Jt;
VECTOR Ho, balancepoint;
VECTOR hw = {3, 1, hw_d, 0}; //vector_alloc(3);
VECTOR hb = {3, 1, hb_d, 0}; //vector_alloc(3);
VECTOR H = {3, 1, H_d, 0}; //vector_alloc(3);
VECTOR He = {3, 1, He_d, 0}; //vector_alloc(3);
VECTOR Y2 = {3, 1, Y2_d, 0}; //vector_alloc(3);
VECTOR Z2 = {3, 1, Z2_d, 0}; //vector_alloc(3);
VECTOR Y1 = {3, 1, Y1_d, 0}; //vector_alloc(3);
VECTOR tempY2 = {3, 1, tempY2_d, 0}; //vector_alloc(3);
VECTOR d = {3, 1, d_d, 0}; //vector_alloc(3);
VECTOR dm = {3, 1, dm_d, 0}; //vector_alloc(3);
VECTOR tauh = {3, 1, tauh_d, 0}; //vector_alloc(3);
VECTOR tempg_b = {3, 1, tempg_b_d, 0}; //vector_alloc(3);
VECTOR temp_what = {3, 1, temp_what_d, 0}; //vector_alloc(3);
VECTOR qe = {4, 1, qe_d, 0}; //vector_alloc(4);
VECTOR ae = {3, 1, ae_d, 0}; //vector_alloc(3);
VECTOR we = {3, 1, we_d, 0}; //vector_alloc(3);
VECTOR temp_qconj = {4, 1, temp_qconj_d, 0}; //vector_alloc(4);
VECTOR u = {3, 1, u_d, 0}; //vector_alloc(3);
VECTOR a = {3, 1, a_d, 0}; //vector_alloc(3);
/ /% PID control
//tau_pid = CTRL.Jt * (CTRL.Kp.*ae + CTRL.Kd.*we + CTRL.Ki.*Y1) ;
for(i=0; i<3; i++)
ELV(&a, i+1) = CTRL.Kp[i]*ELV(&ae, i+1) + CTRL.Kd[i]*ELV(&we, i+1) + CTRL.Ki[i]*ELV(&Y1, i+1);
matrix_mac(&Jt, &a, &tau_pid);
//% gyroscopic torque
//taugy = cross(what, H) ;
vcross(what, &H, &taugy);
//% gravity gradient torque
//taugg = CTRL.cgg * cross(g_b, CTRL.Jt*g_b) ;
matrix_mac(&Jt, &g_b, &taugg);
vcross(&g_b, &taugg, &taugg);
vector_scale(CTRL.cgg, &taugg);
//% attitude control torque
//tauc = tau_pid + taugy - taugg ;
vector_add(&tau_pid, &taugy, &tauc);
vector_sub(&tauc, &taugg, &tauc);
//% commanded wheel torque, body axes
//taub = tauh - tauc ;
vector_sub(&tauh, &tauc, &taub);
// Inject sine wave torque, body coordinates
torque_injector(1, &taub);
//% commanded wheel torque, wheel axes
//tauw = CTRL.Ww_b * taub ;
vector_clear(tauw);
matrix_mac(tomatrix(3, 3, CTRL.Ww_b, &Ww_b), &taub, (MATRIX *)tauw);
// Inject sine wave torque, wheel coordinates
torque_injector(2, tauw);
Living with this Code
• The algorithms are the abstractions
• Currently worked on by one engineer
• Printfs for debugging
• Code commented out when no longer used
• Get it working, get it working in the lab (not
simulations)
• Leave well enough alone…
– No refactoring
Technical Debt
“ It’s easy to accrue. It’s
hard to payoff. What’s
lacking is the enforcer from
the loan shark. And the
good business reasons you
had for accruing it in the
first place are likely still
there when you have to
decide if you are going to
fix it or do something else.
—Allen Wirfs-Brock,
editor of the ECMAScript standard
Living with the Ugly:
A Story of Extending JavaScript
How can you define new, compatible functions?
array.fill()—set a span of elements starting
somewhere with a single value
array.transfer()—move a span of elements from
one position to another position within the
same array
An Ugly Challenge
• slice is an existing function that takes a start
and an end-position argument.
• splice is an existing function that takes a start
and length argument.
• indexOf is a function that returns either a
position or -1.
The Ugly Details:
Nothing is Consistent, Lots of Defaults
array.splice(startIndex, howManyToRemove, Optional
elements to add…)
Adds/removes items to/from an array,
and returns the removed item(s)
start index can be negative
If negative, count back from the end
of the array
end index is optional
array.indexof(searchItem, startIndex)
start index can be negative
start index is optional
returns -1 if item not found
array.slice(startIndex, endIndex)
Returns an array starting at the given start
argument, and ends at, but does not include, the
given end argument
Searches array for specified item and returns
its position.
start index can be negative
If negative, count back from the end
of the array
How many to remove can be 0.
If zero, add elements at start index.
Fill
fill(value, start=0, count=this.length-start)
/*
Every element of array from start up to but not including start+count is assigned value.
Start and count are coerced to Number and truncated to integer values.
Negative count is treated as if it was 0.
Negative start values are converted to positive indices relative to the length of the array:
if (start<0) start = this.length-start
Reference to start and count below assume that conversion has already been applied
If count==0 no elements are modified.
If start+count>this.length and this.length is read-only a Range error is thrown and no
elements are modified.
If start+count>this.length and this.length is not read-only, this.length is set to start+count.
Array elements are set sequentially starting with the start index.
The array is returned as the value of this method
*/
Examples
aFloatArray.fill(Infinity); //Fill all elements of a Typed Array with a
value.
aFloatArray.fill(-1,6); //Fill all elements of a Typed Array starting at
element 6 with -1.
aFloatArray(1.5, 1, 5); //Fill the first 5 elements of a Typed Array.
[ ].fill("abc",0,12).fill("xyz",-1,12); //Create a regular array, fill its first
dozen elements with "abc", and its 2nd dozen elements with "xyz”
Design Rationale
• Placing the fill value first means no indices need to be
specified when filling all elements. (Reasonable
Defaults for optional arguments)
• Follows start/count argument pattern similar to Array
splice. (Compatible with some existing function)
• I initially tried the slice argument style assuming that
this would have more utility when combined with
search functions that return array indices (indexOf,
findIndex, etc). But they return -1 to indicate failure
which isn't a good fit with the slice style.
• The start+count model seems conceptually simpler to
understand and specify.
Transfer
transfer(target=0,source=0, count=this.length-source)
/*
The sequence of array elements from source index up to but not including source+count are transferred within
the array to the span of elements starting at the target index.
The length of the array is not modified.
The transfer takes into account the possibility that the source and target ranges overlap.
source, target, and count are coerced to Number and truncated to integer values.
Negative source and target indices are converted to positive indices relative to the length of the array.
If count==0 no elements are modified.
If source+count>this.length a Range error is thrown and no elements are modified.
If target+count>this.length and this.length is read-only a Range error is thrown and no elements are modified.
If target+count>this.length and this.length is not read-only, this.length is set to target+count.
Array elements are sequentially transferred in a manner appropriate to avoid overlap conflicts. If target<=source
a left to right
transfer is performed. If target>source a right to left transfer is performed.
If a target element is encountered that cannot be assigned, a type error is thrown and no additional elements are
modified.
Missing elements are processed as if they had the value undefined. Sparse array "holes" are transferred just like
for other array functions.
The array is returned as the value of this method */
Examples
[0,1,2,3,4].transfer(0,2); //[2,3,4,3,4]
[0,1,2,3,4].transfer(2,0,2); //[0,1,0,1,4]
[0,1,2].transfer(1); // [0,0,1,2]
Int8Array.from([0,1,2]).transfer(1); //RangeError
Int8Array.from([0,1,2]).transfer(1,0,2); // Int8Array 0,0,1
Int8Array.from([0,1,2]).transfer(0,1,2); // Int8Array 1,2,2
Design Rationale
• I considered "copy" and "move" names. Copy seems confusing. People
might expect that anArray.copy(1,10) might create a new
array, essentially meaning the same thing as Array.from(anArray). Move
suggests a transfer that removes the source rather than replicating
it. "transfer” doesn't carry those connotations.
• See the fill rationale for why the slice argument patterns isn't used.
• Defaulting count to this.length-source seems easiest to explain. There
might be some utility in making the default count be min(this.length-
source,this.length-target) but it could cause problems.
• I considered a final optional "fill" parameter which would provide a
value to put into each source element after it was copied (taking overlap
into account). This would make it more like a true "move"
operation. However, I could not convince myself that there was enough
utility to justify the added complexity. (Do the simplest reasonable
thing)
http://xkcd.com/844/
“Now remember, things look bad and it looks like
you’re not gonna make it, then you gotta get mean.
I mean plumb, mad-dog mean. ‘Cause if you lose
your head and you give up then you neither live nor
win. That’s just the way it is.”
-Clint Eastwood, The Outlaw Josey Wales
Conclusions
• Over time, code gets more complex
• It’s easier to clean up as you go
• Hard, not impossible, to clean up later
• Don’t alwaysdo the “easiest” thing
• Prepare to do the greater good
• Make your goal ongoing sustainability

More Related Content

What's hot

Devoxx France: Fault tolerant microservices on the JVM with Cassandra
Devoxx France: Fault tolerant microservices on the JVM with CassandraDevoxx France: Fault tolerant microservices on the JVM with Cassandra
Devoxx France: Fault tolerant microservices on the JVM with CassandraChristopher Batey
 
Voxxed Vienna 2015 Fault tolerant microservices
Voxxed Vienna 2015 Fault tolerant microservicesVoxxed Vienna 2015 Fault tolerant microservices
Voxxed Vienna 2015 Fault tolerant microservicesChristopher Batey
 
GWT training session 3
GWT training session 3GWT training session 3
GWT training session 3SNEHAL MASNE
 
A Series of Fortunate Events - PHP Benelux Conference 2015
A Series of Fortunate Events - PHP Benelux Conference 2015A Series of Fortunate Events - PHP Benelux Conference 2015
A Series of Fortunate Events - PHP Benelux Conference 2015Matthias Noback
 
【第一季第二期】Dive into javascript event
【第一季第二期】Dive into javascript event【第一季第二期】Dive into javascript event
【第一季第二期】Dive into javascript eventtbosstraining
 
Mario Fusco - Reactive programming in Java - Codemotion Milan 2017
Mario Fusco - Reactive programming in Java - Codemotion Milan 2017Mario Fusco - Reactive programming in Java - Codemotion Milan 2017
Mario Fusco - Reactive programming in Java - Codemotion Milan 2017Codemotion
 
Testing in android
Testing in androidTesting in android
Testing in androidjtrindade
 
Introduction to React for jQuery Developers
Introduction to React for jQuery DevelopersIntroduction to React for jQuery Developers
Introduction to React for jQuery DevelopersRonald Huereca
 
Reactive programming every day
Reactive programming every dayReactive programming every day
Reactive programming every dayVadym Khondar
 
Cloud patterns - NDC Oslo 2016 - Tamir Dresher
Cloud patterns - NDC Oslo 2016 - Tamir DresherCloud patterns - NDC Oslo 2016 - Tamir Dresher
Cloud patterns - NDC Oslo 2016 - Tamir DresherTamir Dresher
 
Creating a Java EE 7 Websocket Chat Application
Creating a Java EE 7 Websocket Chat ApplicationCreating a Java EE 7 Websocket Chat Application
Creating a Java EE 7 Websocket Chat ApplicationMicha Kops
 
Kogito: cloud native business automation
Kogito: cloud native business automationKogito: cloud native business automation
Kogito: cloud native business automationMario Fusco
 
Java Svet - Communication Between Android App Components
Java Svet - Communication Between Android App ComponentsJava Svet - Communication Between Android App Components
Java Svet - Communication Between Android App ComponentsAleksandar Ilić
 
Exploring Twitter's Finagle technology stack for microservices
Exploring Twitter's Finagle technology stack for microservicesExploring Twitter's Finagle technology stack for microservices
Exploring Twitter's Finagle technology stack for microservices💡 Tomasz Kogut
 

What's hot (20)

Rxjs ngvikings
Rxjs ngvikingsRxjs ngvikings
Rxjs ngvikings
 
Devoxx France: Fault tolerant microservices on the JVM with Cassandra
Devoxx France: Fault tolerant microservices on the JVM with CassandraDevoxx France: Fault tolerant microservices on the JVM with Cassandra
Devoxx France: Fault tolerant microservices on the JVM with Cassandra
 
Voxxed Vienna 2015 Fault tolerant microservices
Voxxed Vienna 2015 Fault tolerant microservicesVoxxed Vienna 2015 Fault tolerant microservices
Voxxed Vienna 2015 Fault tolerant microservices
 
GWT training session 3
GWT training session 3GWT training session 3
GWT training session 3
 
A Series of Fortunate Events - PHP Benelux Conference 2015
A Series of Fortunate Events - PHP Benelux Conference 2015A Series of Fortunate Events - PHP Benelux Conference 2015
A Series of Fortunate Events - PHP Benelux Conference 2015
 
【第一季第二期】Dive into javascript event
【第一季第二期】Dive into javascript event【第一季第二期】Dive into javascript event
【第一季第二期】Dive into javascript event
 
Mario Fusco - Reactive programming in Java - Codemotion Milan 2017
Mario Fusco - Reactive programming in Java - Codemotion Milan 2017Mario Fusco - Reactive programming in Java - Codemotion Milan 2017
Mario Fusco - Reactive programming in Java - Codemotion Milan 2017
 
Testing in android
Testing in androidTesting in android
Testing in android
 
Introduction to React for jQuery Developers
Introduction to React for jQuery DevelopersIntroduction to React for jQuery Developers
Introduction to React for jQuery Developers
 
Reactive programming every day
Reactive programming every dayReactive programming every day
Reactive programming every day
 
Rxjs swetugg
Rxjs swetuggRxjs swetugg
Rxjs swetugg
 
Cloud patterns - NDC Oslo 2016 - Tamir Dresher
Cloud patterns - NDC Oslo 2016 - Tamir DresherCloud patterns - NDC Oslo 2016 - Tamir Dresher
Cloud patterns - NDC Oslo 2016 - Tamir Dresher
 
Creating a Java EE 7 Websocket Chat Application
Creating a Java EE 7 Websocket Chat ApplicationCreating a Java EE 7 Websocket Chat Application
Creating a Java EE 7 Websocket Chat Application
 
Kogito: cloud native business automation
Kogito: cloud native business automationKogito: cloud native business automation
Kogito: cloud native business automation
 
Rxjs vienna
Rxjs viennaRxjs vienna
Rxjs vienna
 
Java Svet - Communication Between Android App Components
Java Svet - Communication Between Android App ComponentsJava Svet - Communication Between Android App Components
Java Svet - Communication Between Android App Components
 
Knative Outro
Knative OutroKnative Outro
Knative Outro
 
20090315 Comet
20090315 Comet20090315 Comet
20090315 Comet
 
Exploring Twitter's Finagle technology stack for microservices
Exploring Twitter's Finagle technology stack for microservicesExploring Twitter's Finagle technology stack for microservices
Exploring Twitter's Finagle technology stack for microservices
 
Firebase ng2 zurich
Firebase ng2 zurichFirebase ng2 zurich
Firebase ng2 zurich
 

Similar to Maintaining Your Code Clint Eastwood Style

Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STMConcurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STMMario Fusco
 
softshake 2014 - Java EE
softshake 2014 - Java EEsoftshake 2014 - Java EE
softshake 2014 - Java EEAlexis Hassler
 
An approach to responsive, realtime with Backbone.js and WebSockets
An approach to responsive, realtime with Backbone.js and WebSocketsAn approach to responsive, realtime with Backbone.js and WebSockets
An approach to responsive, realtime with Backbone.js and WebSocketsAndrei Sebastian Cîmpean
 
Hazelcast and MongoDB at Cloud CMS
Hazelcast and MongoDB at Cloud CMSHazelcast and MongoDB at Cloud CMS
Hazelcast and MongoDB at Cloud CMSuzquiano
 
Pushing Datatothe Browserwith Comet Ajax W
Pushing Datatothe Browserwith Comet Ajax WPushing Datatothe Browserwith Comet Ajax W
Pushing Datatothe Browserwith Comet Ajax Wrajivmordani
 
Legacy Code Kata v3.0
Legacy Code Kata v3.0Legacy Code Kata v3.0
Legacy Code Kata v3.0William Munn
 
Java Svet - Communication Between Android App Components
Java Svet - Communication Between Android App ComponentsJava Svet - Communication Between Android App Components
Java Svet - Communication Between Android App ComponentsPSTechSerbia
 
Building Web Apps Sanely - EclipseCon 2010
Building Web Apps Sanely - EclipseCon 2010Building Web Apps Sanely - EclipseCon 2010
Building Web Apps Sanely - EclipseCon 2010Chris Ramsdale
 
Realtime html5 multiplayer_games_with_node_js
Realtime html5 multiplayer_games_with_node_jsRealtime html5 multiplayer_games_with_node_js
Realtime html5 multiplayer_games_with_node_jsMario Gonzalez
 
Creating a Facebook Clone - Part XXV - Transcript.pdf
Creating a Facebook Clone - Part XXV - Transcript.pdfCreating a Facebook Clone - Part XXV - Transcript.pdf
Creating a Facebook Clone - Part XXV - Transcript.pdfShaiAlmog1
 
Anton Moldovan "Load testing which you always wanted"
Anton Moldovan "Load testing which you always wanted"Anton Moldovan "Load testing which you always wanted"
Anton Moldovan "Load testing which you always wanted"Fwdays
 
Real Life Clean Architecture
Real Life Clean ArchitectureReal Life Clean Architecture
Real Life Clean ArchitectureMattia Battiston
 
Clean Code: Chapter 3 Function
Clean Code: Chapter 3 FunctionClean Code: Chapter 3 Function
Clean Code: Chapter 3 FunctionKent Huang
 
Automated acceptance test
Automated acceptance testAutomated acceptance test
Automated acceptance testBryan Liu
 

Similar to Maintaining Your Code Clint Eastwood Style (20)

Lambdas puzzler - Peter Lawrey
Lambdas puzzler - Peter LawreyLambdas puzzler - Peter Lawrey
Lambdas puzzler - Peter Lawrey
 
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STMConcurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
Concurrency, Scalability & Fault-tolerance 2.0 with Akka Actors & STM
 
softshake 2014 - Java EE
softshake 2014 - Java EEsoftshake 2014 - Java EE
softshake 2014 - Java EE
 
An approach to responsive, realtime with Backbone.js and WebSockets
An approach to responsive, realtime with Backbone.js and WebSocketsAn approach to responsive, realtime with Backbone.js and WebSockets
An approach to responsive, realtime with Backbone.js and WebSockets
 
Hazelcast and MongoDB at Cloud CMS
Hazelcast and MongoDB at Cloud CMSHazelcast and MongoDB at Cloud CMS
Hazelcast and MongoDB at Cloud CMS
 
Pushing Datatothe Browserwith Comet Ajax W
Pushing Datatothe Browserwith Comet Ajax WPushing Datatothe Browserwith Comet Ajax W
Pushing Datatothe Browserwith Comet Ajax W
 
Legacy Code Kata v3.0
Legacy Code Kata v3.0Legacy Code Kata v3.0
Legacy Code Kata v3.0
 
Devoxx 2012 (v2)
Devoxx 2012 (v2)Devoxx 2012 (v2)
Devoxx 2012 (v2)
 
Clean Architecture @ Taxibeat
Clean Architecture @ TaxibeatClean Architecture @ Taxibeat
Clean Architecture @ Taxibeat
 
Saving lives with rx java
Saving lives with rx javaSaving lives with rx java
Saving lives with rx java
 
Java Svet - Communication Between Android App Components
Java Svet - Communication Between Android App ComponentsJava Svet - Communication Between Android App Components
Java Svet - Communication Between Android App Components
 
Bot builder v4 HOL
Bot builder v4 HOLBot builder v4 HOL
Bot builder v4 HOL
 
Building Web Apps Sanely - EclipseCon 2010
Building Web Apps Sanely - EclipseCon 2010Building Web Apps Sanely - EclipseCon 2010
Building Web Apps Sanely - EclipseCon 2010
 
Realtime html5 multiplayer_games_with_node_js
Realtime html5 multiplayer_games_with_node_jsRealtime html5 multiplayer_games_with_node_js
Realtime html5 multiplayer_games_with_node_js
 
Creating a Facebook Clone - Part XXV - Transcript.pdf
Creating a Facebook Clone - Part XXV - Transcript.pdfCreating a Facebook Clone - Part XXV - Transcript.pdf
Creating a Facebook Clone - Part XXV - Transcript.pdf
 
Anton Moldovan "Load testing which you always wanted"
Anton Moldovan "Load testing which you always wanted"Anton Moldovan "Load testing which you always wanted"
Anton Moldovan "Load testing which you always wanted"
 
Real Life Clean Architecture
Real Life Clean ArchitectureReal Life Clean Architecture
Real Life Clean Architecture
 
Clean Code: Chapter 3 Function
Clean Code: Chapter 3 FunctionClean Code: Chapter 3 Function
Clean Code: Chapter 3 Function
 
Android wearpp
Android wearppAndroid wearpp
Android wearpp
 
Automated acceptance test
Automated acceptance testAutomated acceptance test
Automated acceptance test
 

Recently uploaded

Architecture case study India Habitat Centre, Delhi.pdf
Architecture case study India Habitat Centre, Delhi.pdfArchitecture case study India Habitat Centre, Delhi.pdf
Architecture case study India Habitat Centre, Delhi.pdfSumit Lathwal
 
毕业文凭制作#回国入职#diploma#degree澳洲弗林德斯大学毕业证成绩单pdf电子版制作修改#毕业文凭制作#回国入职#diploma#degree
毕业文凭制作#回国入职#diploma#degree澳洲弗林德斯大学毕业证成绩单pdf电子版制作修改#毕业文凭制作#回国入职#diploma#degree 毕业文凭制作#回国入职#diploma#degree澳洲弗林德斯大学毕业证成绩单pdf电子版制作修改#毕业文凭制作#回国入职#diploma#degree
毕业文凭制作#回国入职#diploma#degree澳洲弗林德斯大学毕业证成绩单pdf电子版制作修改#毕业文凭制作#回国入职#diploma#degree ttt fff
 
FiveHypotheses_UIDMasterclass_18April2024.pdf
FiveHypotheses_UIDMasterclass_18April2024.pdfFiveHypotheses_UIDMasterclass_18April2024.pdf
FiveHypotheses_UIDMasterclass_18April2024.pdfShivakumar Viswanathan
 
8377877756 Full Enjoy @24/7 Call Girls in Nirman Vihar Delhi NCR
8377877756 Full Enjoy @24/7 Call Girls in Nirman Vihar Delhi NCR8377877756 Full Enjoy @24/7 Call Girls in Nirman Vihar Delhi NCR
8377877756 Full Enjoy @24/7 Call Girls in Nirman Vihar Delhi NCRdollysharma2066
 
DAKSHIN BIHAR GRAMIN BANK: REDEFINING THE DIGITAL BANKING EXPERIENCE WITH A U...
DAKSHIN BIHAR GRAMIN BANK: REDEFINING THE DIGITAL BANKING EXPERIENCE WITH A U...DAKSHIN BIHAR GRAMIN BANK: REDEFINING THE DIGITAL BANKING EXPERIENCE WITH A U...
DAKSHIN BIHAR GRAMIN BANK: REDEFINING THE DIGITAL BANKING EXPERIENCE WITH A U...Rishabh Aryan
 
办理(USYD毕业证书)澳洲悉尼大学毕业证成绩单原版一比一
办理(USYD毕业证书)澳洲悉尼大学毕业证成绩单原版一比一办理(USYD毕业证书)澳洲悉尼大学毕业证成绩单原版一比一
办理(USYD毕业证书)澳洲悉尼大学毕业证成绩单原版一比一diploma 1
 
ARt app | UX Case Study
ARt app | UX Case StudyARt app | UX Case Study
ARt app | UX Case StudySophia Viganò
 
shot list for my tv series two steps back
shot list for my tv series two steps backshot list for my tv series two steps back
shot list for my tv series two steps back17lcow074
 
Top 10 Modern Web Design Trends for 2025
Top 10 Modern Web Design Trends for 2025Top 10 Modern Web Design Trends for 2025
Top 10 Modern Web Design Trends for 2025Rndexperts
 
Passbook project document_april_21__.pdf
Passbook project document_april_21__.pdfPassbook project document_april_21__.pdf
Passbook project document_april_21__.pdfvaibhavkanaujia
 
Design principles on typography in design
Design principles on typography in designDesign principles on typography in design
Design principles on typography in designnooreen17
 
PORTFOLIO DE ARQUITECTURA CRISTOBAL HERAUD 2024
PORTFOLIO DE ARQUITECTURA CRISTOBAL HERAUD 2024PORTFOLIO DE ARQUITECTURA CRISTOBAL HERAUD 2024
PORTFOLIO DE ARQUITECTURA CRISTOBAL HERAUD 2024CristobalHeraud
 
group_15_empirya_p1projectIndustrial.pdf
group_15_empirya_p1projectIndustrial.pdfgroup_15_empirya_p1projectIndustrial.pdf
group_15_empirya_p1projectIndustrial.pdfneelspinoy
 
办理学位证(NTU证书)新加坡南洋理工大学毕业证成绩单原版一比一
办理学位证(NTU证书)新加坡南洋理工大学毕业证成绩单原版一比一办理学位证(NTU证书)新加坡南洋理工大学毕业证成绩单原版一比一
办理学位证(NTU证书)新加坡南洋理工大学毕业证成绩单原版一比一A SSS
 
原版1:1定制堪培拉大学毕业证(UC毕业证)#文凭成绩单#真实留信学历认证永久存档
原版1:1定制堪培拉大学毕业证(UC毕业证)#文凭成绩单#真实留信学历认证永久存档原版1:1定制堪培拉大学毕业证(UC毕业证)#文凭成绩单#真实留信学历认证永久存档
原版1:1定制堪培拉大学毕业证(UC毕业证)#文凭成绩单#真实留信学历认证永久存档208367051
 
办理卡尔顿大学毕业证成绩单|购买加拿大文凭证书
办理卡尔顿大学毕业证成绩单|购买加拿大文凭证书办理卡尔顿大学毕业证成绩单|购买加拿大文凭证书
办理卡尔顿大学毕业证成绩单|购买加拿大文凭证书zdzoqco
 
CREATING A POSITIVE SCHOOL CULTURE CHAPTER 10
CREATING A POSITIVE SCHOOL CULTURE CHAPTER 10CREATING A POSITIVE SCHOOL CULTURE CHAPTER 10
CREATING A POSITIVE SCHOOL CULTURE CHAPTER 10uasjlagroup
 
办理学位证(TheAuckland证书)新西兰奥克兰大学毕业证成绩单原版一比一
办理学位证(TheAuckland证书)新西兰奥克兰大学毕业证成绩单原版一比一办理学位证(TheAuckland证书)新西兰奥克兰大学毕业证成绩单原版一比一
办理学位证(TheAuckland证书)新西兰奥克兰大学毕业证成绩单原版一比一Fi L
 
MT. Marseille an Archipelago. Strategies for Integrating Residential Communit...
MT. Marseille an Archipelago. Strategies for Integrating Residential Communit...MT. Marseille an Archipelago. Strategies for Integrating Residential Communit...
MT. Marseille an Archipelago. Strategies for Integrating Residential Communit...katerynaivanenko1
 
昆士兰大学毕业证(UQ毕业证)#文凭成绩单#真实留信学历认证永久存档
昆士兰大学毕业证(UQ毕业证)#文凭成绩单#真实留信学历认证永久存档昆士兰大学毕业证(UQ毕业证)#文凭成绩单#真实留信学历认证永久存档
昆士兰大学毕业证(UQ毕业证)#文凭成绩单#真实留信学历认证永久存档208367051
 

Recently uploaded (20)

Architecture case study India Habitat Centre, Delhi.pdf
Architecture case study India Habitat Centre, Delhi.pdfArchitecture case study India Habitat Centre, Delhi.pdf
Architecture case study India Habitat Centre, Delhi.pdf
 
毕业文凭制作#回国入职#diploma#degree澳洲弗林德斯大学毕业证成绩单pdf电子版制作修改#毕业文凭制作#回国入职#diploma#degree
毕业文凭制作#回国入职#diploma#degree澳洲弗林德斯大学毕业证成绩单pdf电子版制作修改#毕业文凭制作#回国入职#diploma#degree 毕业文凭制作#回国入职#diploma#degree澳洲弗林德斯大学毕业证成绩单pdf电子版制作修改#毕业文凭制作#回国入职#diploma#degree
毕业文凭制作#回国入职#diploma#degree澳洲弗林德斯大学毕业证成绩单pdf电子版制作修改#毕业文凭制作#回国入职#diploma#degree
 
FiveHypotheses_UIDMasterclass_18April2024.pdf
FiveHypotheses_UIDMasterclass_18April2024.pdfFiveHypotheses_UIDMasterclass_18April2024.pdf
FiveHypotheses_UIDMasterclass_18April2024.pdf
 
8377877756 Full Enjoy @24/7 Call Girls in Nirman Vihar Delhi NCR
8377877756 Full Enjoy @24/7 Call Girls in Nirman Vihar Delhi NCR8377877756 Full Enjoy @24/7 Call Girls in Nirman Vihar Delhi NCR
8377877756 Full Enjoy @24/7 Call Girls in Nirman Vihar Delhi NCR
 
DAKSHIN BIHAR GRAMIN BANK: REDEFINING THE DIGITAL BANKING EXPERIENCE WITH A U...
DAKSHIN BIHAR GRAMIN BANK: REDEFINING THE DIGITAL BANKING EXPERIENCE WITH A U...DAKSHIN BIHAR GRAMIN BANK: REDEFINING THE DIGITAL BANKING EXPERIENCE WITH A U...
DAKSHIN BIHAR GRAMIN BANK: REDEFINING THE DIGITAL BANKING EXPERIENCE WITH A U...
 
办理(USYD毕业证书)澳洲悉尼大学毕业证成绩单原版一比一
办理(USYD毕业证书)澳洲悉尼大学毕业证成绩单原版一比一办理(USYD毕业证书)澳洲悉尼大学毕业证成绩单原版一比一
办理(USYD毕业证书)澳洲悉尼大学毕业证成绩单原版一比一
 
ARt app | UX Case Study
ARt app | UX Case StudyARt app | UX Case Study
ARt app | UX Case Study
 
shot list for my tv series two steps back
shot list for my tv series two steps backshot list for my tv series two steps back
shot list for my tv series two steps back
 
Top 10 Modern Web Design Trends for 2025
Top 10 Modern Web Design Trends for 2025Top 10 Modern Web Design Trends for 2025
Top 10 Modern Web Design Trends for 2025
 
Passbook project document_april_21__.pdf
Passbook project document_april_21__.pdfPassbook project document_april_21__.pdf
Passbook project document_april_21__.pdf
 
Design principles on typography in design
Design principles on typography in designDesign principles on typography in design
Design principles on typography in design
 
PORTFOLIO DE ARQUITECTURA CRISTOBAL HERAUD 2024
PORTFOLIO DE ARQUITECTURA CRISTOBAL HERAUD 2024PORTFOLIO DE ARQUITECTURA CRISTOBAL HERAUD 2024
PORTFOLIO DE ARQUITECTURA CRISTOBAL HERAUD 2024
 
group_15_empirya_p1projectIndustrial.pdf
group_15_empirya_p1projectIndustrial.pdfgroup_15_empirya_p1projectIndustrial.pdf
group_15_empirya_p1projectIndustrial.pdf
 
办理学位证(NTU证书)新加坡南洋理工大学毕业证成绩单原版一比一
办理学位证(NTU证书)新加坡南洋理工大学毕业证成绩单原版一比一办理学位证(NTU证书)新加坡南洋理工大学毕业证成绩单原版一比一
办理学位证(NTU证书)新加坡南洋理工大学毕业证成绩单原版一比一
 
原版1:1定制堪培拉大学毕业证(UC毕业证)#文凭成绩单#真实留信学历认证永久存档
原版1:1定制堪培拉大学毕业证(UC毕业证)#文凭成绩单#真实留信学历认证永久存档原版1:1定制堪培拉大学毕业证(UC毕业证)#文凭成绩单#真实留信学历认证永久存档
原版1:1定制堪培拉大学毕业证(UC毕业证)#文凭成绩单#真实留信学历认证永久存档
 
办理卡尔顿大学毕业证成绩单|购买加拿大文凭证书
办理卡尔顿大学毕业证成绩单|购买加拿大文凭证书办理卡尔顿大学毕业证成绩单|购买加拿大文凭证书
办理卡尔顿大学毕业证成绩单|购买加拿大文凭证书
 
CREATING A POSITIVE SCHOOL CULTURE CHAPTER 10
CREATING A POSITIVE SCHOOL CULTURE CHAPTER 10CREATING A POSITIVE SCHOOL CULTURE CHAPTER 10
CREATING A POSITIVE SCHOOL CULTURE CHAPTER 10
 
办理学位证(TheAuckland证书)新西兰奥克兰大学毕业证成绩单原版一比一
办理学位证(TheAuckland证书)新西兰奥克兰大学毕业证成绩单原版一比一办理学位证(TheAuckland证书)新西兰奥克兰大学毕业证成绩单原版一比一
办理学位证(TheAuckland证书)新西兰奥克兰大学毕业证成绩单原版一比一
 
MT. Marseille an Archipelago. Strategies for Integrating Residential Communit...
MT. Marseille an Archipelago. Strategies for Integrating Residential Communit...MT. Marseille an Archipelago. Strategies for Integrating Residential Communit...
MT. Marseille an Archipelago. Strategies for Integrating Residential Communit...
 
昆士兰大学毕业证(UQ毕业证)#文凭成绩单#真实留信学历认证永久存档
昆士兰大学毕业证(UQ毕业证)#文凭成绩单#真实留信学历认证永久存档昆士兰大学毕业证(UQ毕业证)#文凭成绩单#真实留信学历认证永久存档
昆士兰大学毕业证(UQ毕业证)#文凭成绩单#真实留信学历认证永久存档
 

Maintaining Your Code Clint Eastwood Style

  • 2. “Whatever success I've had is due to a lot of instinct and a little luck.” —Clint Eastwood
  • 3. “Whatever success I’ve had is through a lot of hard work and a little luck.” —Rebecca Wirfs-Brock
  • 5. “I like the notion of working the program, like an artist works a lump of clay..” —Ward Cunningham
  • 6.
  • 7. Subscription Quantity Interface: Version 1 public interface SubscriptionQuantity { public Integer getQuantity(); public String getUnits(); public DeliveryBasis getDeliveryBasis(); public String toString(); }
  • 9. Subscription Quantity Implementation: Version 1 @Override public TimeBasedQuantity add(TimeBasedQuantity amount) { return new MonthQuantity(getQuantity() + amount.asMonths().getQuantity()); } @Override public TimeBasedQuantity add(TimeBasedQuantity amount) { if (amount.getUnits() == getUnits()) {return new YearQuantity(getQuantity() + amount.getQuantity());} else {return new MonthQuantity(asMonths().getQuantity() + amount.getQuantity());} } Adding to a Year returns a new Month Quantity unless units are Years Adding to a Month Quantity returns a new Month Quantity public abstract TimeBasedQuantity add(TimeBasedQuantity amount); Time Based Quantity declares add must be implemented by subclasses
  • 10.
  • 11. Subscription Quantity Implementation: Version 1 public SubscriptionQuantity add(SubscriptionQuantity amount) { if (amount.getDeliveryBasis() != getDeliveryBasis()) {throw new IllegalArgumentException("Cannot subtract issues from time");} else return (add((TimeBasedQuantity) amount)); } public TimeBasedQuantity add(TimeBasedQuantity amount) { return new MonthQuantity(getQuantity() + amount.asMonths().getQuantity()); } Adding to a Year returns a new Month Quantity unless units are Years Adding to a Time Based Quantity public TimeBasedQuantity add(TimeBasedQuantity amount) { if (amount.getUnits() == getUnits()) { return new YearQuantity(getQuantity() + amount.getQuantity());} else {return new MonthQuantity(asMonths().getQuantity() + amount.getQuantity());} } Adding to a Month Quantity returns a new Month Quantity
  • 12. The Current Definition public interface SubscriptionQuantity { public abstract Integer getQuantity(); public abstract String getUnits(); public abstract DeliveryBasis getDeliveryBasis(); public abstract String toString(); public abstract SubscriptionQuantity multiplyBy(int amount); public abstract SubscriptionQuantity multiplyBy(SubscriptionQuantity amount); public abstract SubscriptionQuantity subtract(int amount); public abstract SubscriptionQuantity subtract(SubscriptionQuantity amount); public abstract SubscriptionQuantity add(int amount); public abstract SubscriptionQuantity add(SubscriptionQuantity amount); public abstract boolean lessThan(SubscriptionQuantity amount); public abstract boolean greaterThan(SubscriptionQuantity amount); }
  • 13. “So today, let's write a program simply. But let's also realize that tomorrow, we're going to make it more complex, because tomorrow it's going to do more” —Ward Cunningham
  • 14. A Story of Consistent Error Reporting Evolution • Sprint 1: Ad hoc error detection in domain entities and services – Sometimes setters threw exceptions – Sometimes they did not check for anything (fail fast approach) – This was sprint 1 after all! • Sprint 2: Some design discipline, but maybe not all good ideas – Well-formed domain object constructors don’t allow invalid state – Attribute setter methods throw exceptions – Services catch errors and do their best to report them – Problem: Sometimes objects possibly can get into inconsistent states – Problem: Sometimes not all errors are detected (bailing early)
  • 15. Ongoing Evolution • Sprint 3 – ErrorReporter – ErrorReportingService abstraction – All services designed to collect and report all errors detected during handling of a request • Sprint 4: – User error messages declared in an external resource – Ability to add parameter values into messages to improve logging and support localization • Coming soon??? – ErrorValidator classes (check for cross-attribute constraints)
  • 16. public class ErrorReportingService implements ErrorReporter { private MessageHolder errorMessages = new MessageHolder(); public boolean hasErrors() {return (errorMessages.size() > 0);} public boolean hasErrorMessage(String errorKey) { return errorMessages.containsKey(errorKey);} public void addErrorMessage(String key, String message) { errorMessages.addMessage(key, message);} public String getErrorMessage(String errorKey) { return errorMessages.getMessage(errorKey);} public Set<String> getErrorMessageKeys() { return errorMessages.keySet();} /** * If any errors have been accumulated, throws MultipleMessageException holding * the corresponding error messages. * @throws MultipleMessageException */ protected void handleErrors() throws MultipleMessageException { if (hasErrors()) {MultipleMessageException exception = loadMultipleMessageException(new MultipleMessageException()); throw exception;} } /** * @param loadable - exception into which this service's error messages are to be copied. * @return MultipleMessageException holding the same error messages as this service. */ protected MultipleMessageException loadMultipleMessageException(MultipleMessageException loadable) { for (String nextKey : getErrorMessageKeys()) { loadable.addErrorMessage(nextKey, getErrorMessage(nextKey));} return loadable; } ErrorReporting Service
  • 17. public Account createAccount( User user, String customerName, PaymentType paymentType, CheckPaymentDetails preferredCheckingDetails, CreditCardPaymentDetails preferredCreditCardDetails, Address shippingAddress, Address billingAddress) throws MultipleMessageException { // cross-field validation. verifyRequiredParameters(user,paymentType,preferredCheckingDetails,preferredCreditCardDetails,shippingAddress,billingAddress); handleErrors(); Account account = new Account(customerName); account.setUser(user); account.setPreferedPaymentMethod(paymentType); account.setCheckPaymentInfo(preferredCheckingDetails); account.setCheckPaymentInfo(preferredCreditCardDetails); account.setShippingAddress(shippingAddress); account.setBillingAddress(billingAddress); handleErrors(); // Persist. try {account = getRepository().add(account); } catch (AccountAlreadyExistsException accountAlreadyExistsException) { addErrorMessage(ACCOUNT_ALREADY_EXISTS_ERROR, accountAlreadyExistsException.getMessage()); } handleErrors(); return account; } Creating an Account
  • 18. “Sometimes if you want to see a change for the better, you have to take things into your own hands.” —Clint Eastwood
  • 19. The Broken Window Theory “If the windows are not repaired, the tendency is for vandals to break a few more windows.” — James Q. Wilson & George L. Kelling
  • 20. Small Irritations Corrected @Test (expected = IllegalArgumentException.class) public void testEmailAddressMustIncludeAtSign(){ String username = "rebeccawb"; String password = "abAB123~"; String emailAddress = "rebecca.wirfs-brock.com"; new User(username, password, emailAddress); } @Test public void testEmailAddressMustIncludeAtSign(){ String username = "rebeccawb"; String password = "abAB123~"; String emailAddress = "rebecca.wirfs-brock.com"; expect(IllegalArgumentException.class); new User(username, password, emailAddress); } “Fixed warnings about unused variables by using JUnit 4 annotations for expected exceptions and by removing variables that are never used, because exception is expected in constructor.”
  • 21. A Rhythm of Restructuring, Refactoring and Ongoing CleanupDate: April 15, 2013 11:35:46 PM PDT Refactored package structure to group classes into packages organized by layer of the architecture: domain, service, repository. Date: April 17, 2013 8:25:51 PM PDT Made class variables private Date: April 20, 2013 3:19:39 PM PDT Implemented AccountRepository. Refactored test data, so that it can be shared between tests. Cleaned up UserRepositoryStub. Changed comments in Account to Javadoc comments. Date: April 18, 2013 12:31:18 AM PDT Refactor error messages in Entity objects by using new MessageHandler class and ErrorReporter interface. Specify AccountService interface to create Account. Date: April 22, 2013 2:47:35 PM PDT Removed print statements from tests. Date: April 22, 2013 9:56:10 AM PDT Fixed password encryption by using a fixed salt generator and a digester class instead of using the standard password encryptors. In a production system we would use more complicating salting algorithms, but this demonstrates wrapping a library in a service
  • 22. “Good code is its own best documentation.” —Steve McConnell
  • 23. Well maybe…. public void setPassword(String newPassword) { if (newPassword == null) {throw new IllegalArgumentException("The password may not be set to null.");} newPassword = newPassword.trim(); if (newPassword.matches("[a-zA-Z]{1}")) { throw new IllegalArgumentException("The password must contain at least one alpha character.");} if (!newPassword.matches("^.*d.+$")) { throw new IllegalArgumentException("The password must contain at least one digit.");} if (newPassword.length() < 6 || newPassword.length() > 32){ throw new IllegalArgumentException("The password must be 6 - 32 character and contain at least one letter and one number.");} if (newPassword.matches("^.*s.*$")) { throw new IllegalArgumentException("The password must not contain space or non-printing characters.");} this.password = newPassword; } My exception messages documented the rules. Without them, I’d be lost. Regex expressions are not documentation, no matter how straightforward
  • 24. private void addBuildingNameFloorRoomNumberLine() { String buildingName = address.getBuildingName(); String floor = address.getFloor(); String room = address.getRoomNumber(); Boolean buildingNameExists, floorExists, roomExists = false; buildingNameExists = buildingName.length() != 0; if (buildingNameExists){ builder.append(buildingName); buildingNameExists = true; } floorExists = floor.length() !=0; if (floorExists) { if (buildingNameExists) {builder.append(", ");} builder.append(floor); } roomExists = room.length() !=0; if (roomExists) { if (floorExists) { builder.append(", ");} else if (buildingNameExists) {builder.append(", ");} builder.append(room); } if (buildingNameExists || floorExists || roomExists) builder.append("n"); } OK…. Some “good” code is just tedious. But you can do things to make it more legible. Good code is not “beautiful code”. Do not confuse goodness with beauty.
  • 25. “I tried being reasonable. I didn’t like it.” —Clint Eastwood as Dirty Harry
  • 26. Do Style Guides Help? “Go ahead, make my day.” —Clint Eastwoodin Sudden Impact http://source.android.com/source/code-style.html
  • 27. “Respect your efforts, respect yourself. Self- respect leads to self-discipline.” —Clint Eastwood
  • 28. Import Statements Style over Substance? • Order: – Android imports first – Then third parties (com, junit, net, org) – Finally, java and avax • Formatting: – Alphabetical within each grouping, with capital letters before lower case letters (e.g. Z before a). – A blank line between each major grouping (android, com, junit, net, org, java, javax). • The fine print: – Use explicit class names instead of *
  • 29. Naming Conventions Style over Substance? The Rules • Start non-public, non-static field names with m. • Start static field names with s. • Start all other fields with a lower case letter • Public static final fields ALL_CAPS_WITH_UNDERSCORES public class MyClass { public static final int SOME_CONSTANT = 42; public int publicField; private static MyClass sSingleton; int mPackagePrivate; private int mPrivate; protected int mProtected; }
  • 30. Field Definitions Do options lead to inconsistency? • Define fields in standard places • Fields should be defined at the top of the file • Or before methods that use them OK, which is preferred?
  • 31. Exception Handling Guidelines Do options lead to inconsistency? • Prime Directive: Never swallow an exception • Don’t catch Exception. Instead catch specific exceptions by name • Prioritized list of handling strategies: 1. Throw a new exception that matches your level of abstraction 2. Handle gracefully and when possible substitute an appropriate default 3. Catch and then rethrow a runtime exception (because your app should crash) 4. If you ignore an exception, say why in a comment
  • 32. “I get lazy or bored, even with good intentions it’s hard to be consistent programming. It’s easier to be a consistent runner.” —Rebecca Wirfs-Brock
  • 33. “Our parting thought: BE CONSISTENT. If you're editing code, take a few minutes to look at the code around you and determine its style.” —The Google App Style Guideline Authors
  • 34. “But local style is also important. If code you add to a file looks drastically different from the existing code around it, it throws readers out of their rhythm when they go to read it. Try to avoid this.” —The Google App Style Guideline Authors
  • 36. HOW DO YOU LIVE SOMEONE ELSE’S BAD CODE?
  • 37. “My first impression was confusion. Some of the code is procedural, some is object- oriented, some global variables are used, and the database is a forest of related tables…. I’ve learned to read and write Moodle, to modify it and expand it with custom plugins, to debug it and figure out what is going on even when the log files aren’t helping at all.” -Olja Petrovic The Apprentice Coder’s Blog Living inside Moodle e-Learning Platform, source code, and DB
  • 38. Making Sense of “Mess-View-Controller” • This isn’t Model-View- Controller • Logic is mixed with presentation in absurd ways • No one else thinks this is a problem What’s already there isn’t going to get fixed. Technically, it isn’t broken. Just bad or awkward or error prone or unreliable or not the way you’d do it “Norms weren’t established. Conventions (if any) grow organically.” “Not what I’m used to”
  • 39. Knowing the code isn’t enough… • You’ve got to crack how the database works and is updated. • It ties everything together – Users and course activities and events – The platform itself – the plugins and blocks and bits and pieces and roles and contexts
  • 41. A Recipe for Making Changes 1. Figure out how a similar activity is done 2. Then: – Write a plugin (enrol or auth) – Add a question type – Add a custom user profile field type – Override a class or function – Hack the core • Safe/easy • Requires deeper knowledge • Done at your own risk
  • 42. HOW DO YOU LIVE WITH YOUR OWN BAD CODE?
  • 43. “When inexperienced developers write bad code, their code is just bad. It has few (if any) redeeming qualities. It often must be scrapped, or at a minimum refactored intensely.” -Brandon Savage Conventional Wisdom
  • 44. Refactoring Experience Report Amr Noaman, Agile 2013 • Initial, approach with three teams: – First, write system/functional automated tests to enable safe refactorings – Then identify design patterns or any other best practice, and transform the code – Keep the codebase working and add features/fix bugs • Results: Failure, abandoned efforts, disappointment, frustration • Observations: – Refactoring objectives too vague – Automated tests were difficult to impossible to write for bad code – Poor planning and measurement – Went too deep with major refactorings without finishing – Lack of management support – Communication problems – Unsustainable development
  • 45. Refactoring Experience Part 2: A more systematic approach • Prepare code to be increasingly refactorable through a simple, continuous, and sustainable set of refactorings
  • 46. Measure Quick-Win Progress Metric Description Unit of Measure Target/ Threshold Code size Number of lines of code excluding comments and white space. KLOC Reaching a plateau for 2 or more iterations. Code Size Reduction Speed (CSRS) Number of lines of code removed per one hour of refactoring. LOC This metric should be stable. Ones it drops, it is an indicator to stop this type of refactoring Number of duplicated code lines Calculated by counting absolute number of lines which are part of at least one (10 LOC or more) duplicate. Absolute Reaching a plateau for 2 or more iterations. Note that not all duplicates can be removed.
  • 47. Refactoring “Rules” • Refactor on the main branch • Limit scope at first: – do quick win refactorings only, – then restructure and write automated component tests • Time allocated to refactoring specified by the senior manager as a % of team effort for each iteration.
  • 48. Measure Examples Removing dead code cost one team 36 hours, and reduced the code size by 6.4%. 46% of their code was duplicated! Refactoring for another project was more challenging due to complex and financially critical problem domain. However, they still removed 40 kloc in 113 hours.
  • 49. New Wisdom: Improvement is better than inertia • Small improvements can lead to bigger ones • Ongoing: – Spend an hour or two “cleaning up” while freeing your mind to do “background” problem solving • Small improvements to do: – Write a test – Refactor a test – Revise a function – Change some code to work better – Factor into smaller chunks – Untangle overly-complicated logic – Remove dead code – …
  • 50. “A good man always knows his limitations.” —Clint Eastwood as Dirty Harry in Magnum Force
  • 51. Sustainable Refactoring • 10 % or less of total time • On the main branch • With management approval/buy in • With identified success criteria • Modest first, then more aggressive • Stop when you get diminished returns
  • 53. At a float altitude of 128,000 feet, the balloon inflates to 40 million cubic feet. Photo Credits: NASA/Nagoya University Thanks Ben Zeiger for sharing the code and his lab where they are building/testing and preparing for 2014 launch. Software to Control the NASA X-ray Telescope InFOCμs
  • 54. Beautiful Technology, Ugly Code • The code: – Originally written in MATLAB – Translated to C – “Light” on comments – Written by electrical engineer/physics guy, not sw dev – Mega-big functions – Lots of calculations – The algorithms work – It’s timing and control code that has bugs – Consequences of bugs: not able to locate accurately, missed repositioning (leading to instability)
  • 55. // ------------------------------------------------------------------------ // TN-4/27/12 New function to resynchronize // serial interface and return error code. PMDuint16 ethSerial_Resync(void* transport_data, PMDuint16 errcode) { HANDLE_TABLE *p = (HANDLE_TABLE *)transport_data; PMDuint16 synccode = ethSerial_Sync(transport_data); /* unsigned short status, statush, statusd, statusdf, mode; PMDGetEventStatus(p->axish, &status); if (status > 1){ char binary[17]; itoa(status, binary, 2); printf("%f: Host: %s Event Status: %sn", get_current_time(), p- >hostname, binary); PMDGetDriveStatus(p->axish, &statusd); itoa(statusd, binary, 2); printf("%f: Host: %s Drive Status: %sn", get_current_time(), p- >hostname, binary); PMDGetDriveFaultStatus(p->axish, &statusdf); printf("Host: %s Drive Fault Status: %#Xn", p->hostname, statusdf); if( statusdf > 0x01){ PMDClearDriveFaultStatus(p->axish); printf("%f: Drive Fault Status Clearedn", get_current_time()); PMDResetEventStatus(p->axish, 0); PMDRestoreOperatingMode(p->axish); } PMDGetOperatingMode(p->axish, &mode); itoa(mode, binary, 2); printf("Host: %s Operating Mode: %sn", p->hostname, binary); PMDGetInstructionError(p->axish, &statush); printf("%f: Host: %s Instruction Error: %#Xn", get_current_time(), p- >hostname, statush ); PMDResetEventStatus(p->axish, 0); } */ //JWM 5/3 Added to diagnose when resync occurs to try to understand why. printf("Resync: %s Due to: %#X , synccode: %#X @: %f n", p->hostname, errcode, synccode, get_current_time()); format_resync_pkt(p->hostname, errcode, synccode, get_current_time());
  • 56. void Controller(double tc, VECTOR *qc, VECTOR *wc, double ttag, VECTOR *qhat, VECTOR *what, RWDATA *RWmeas, LADATA *LAmeas, PVDATA *PVmeas, VECTOR *tauw, VECTOR *dc, VECTOR *ddc, double *tauaz, double *wcaz, VECTOR *dcount, LFCMD *LFcmd, RWACMD *RWAcmd) { double R_d[9], Rt_d[9], g_eci_d[3], g_b_d[3], dmg_d[3], hw_d[3], hb_d[3]; double H_d[3], He_d[3], Y2_d[3], Z2_d[3], Y1_d[3], tempY2_d[3], d_d[3]; double dm_d[3], tauh_d[3], tempg_b_d[3], temp_what_d[3]; double qe_d[4], ae_d[3], we_d[3], temp_qconj_d[4], u_d[3], a_d[3]; double tau_pid_d[3], taugy_d[3], taugg_d[3], tauc_d[3], taub_d[3], tauwc_d[3], qhatp_d[4]; VECTOR g_ecf = {3, 1, PVmeas->p, 0}; //vector_alloc_from_array(3, PVmeas->p); MATRIX R = {3, 3, R_d, 0}; //matrix_alloc(3, 3); MATRIX Rt = {3, 3, Rt_d, 0}; // matrix_alloc(3, 3); VECTOR g_eci = {3, 1, g_eci_d, 0}; //vector_alloc(3); VECTOR g_b = {3, 1, g_b_d, 0}; //vector_alloc(3); VECTOR dmg = {3, 1, dmg_d, 0}; //vector_alloc(3); MATRIX Wb_w, Ww_b, Jt; VECTOR Ho, balancepoint; VECTOR hw = {3, 1, hw_d, 0}; //vector_alloc(3); VECTOR hb = {3, 1, hb_d, 0}; //vector_alloc(3); VECTOR H = {3, 1, H_d, 0}; //vector_alloc(3); VECTOR He = {3, 1, He_d, 0}; //vector_alloc(3); VECTOR Y2 = {3, 1, Y2_d, 0}; //vector_alloc(3); VECTOR Z2 = {3, 1, Z2_d, 0}; //vector_alloc(3); VECTOR Y1 = {3, 1, Y1_d, 0}; //vector_alloc(3); VECTOR tempY2 = {3, 1, tempY2_d, 0}; //vector_alloc(3); VECTOR d = {3, 1, d_d, 0}; //vector_alloc(3); VECTOR dm = {3, 1, dm_d, 0}; //vector_alloc(3); VECTOR tauh = {3, 1, tauh_d, 0}; //vector_alloc(3); VECTOR tempg_b = {3, 1, tempg_b_d, 0}; //vector_alloc(3); VECTOR temp_what = {3, 1, temp_what_d, 0}; //vector_alloc(3); VECTOR qe = {4, 1, qe_d, 0}; //vector_alloc(4); VECTOR ae = {3, 1, ae_d, 0}; //vector_alloc(3); VECTOR we = {3, 1, we_d, 0}; //vector_alloc(3); VECTOR temp_qconj = {4, 1, temp_qconj_d, 0}; //vector_alloc(4); VECTOR u = {3, 1, u_d, 0}; //vector_alloc(3); VECTOR a = {3, 1, a_d, 0}; //vector_alloc(3);
  • 57. / /% PID control //tau_pid = CTRL.Jt * (CTRL.Kp.*ae + CTRL.Kd.*we + CTRL.Ki.*Y1) ; for(i=0; i<3; i++) ELV(&a, i+1) = CTRL.Kp[i]*ELV(&ae, i+1) + CTRL.Kd[i]*ELV(&we, i+1) + CTRL.Ki[i]*ELV(&Y1, i+1); matrix_mac(&Jt, &a, &tau_pid); //% gyroscopic torque //taugy = cross(what, H) ; vcross(what, &H, &taugy); //% gravity gradient torque //taugg = CTRL.cgg * cross(g_b, CTRL.Jt*g_b) ; matrix_mac(&Jt, &g_b, &taugg); vcross(&g_b, &taugg, &taugg); vector_scale(CTRL.cgg, &taugg); //% attitude control torque //tauc = tau_pid + taugy - taugg ; vector_add(&tau_pid, &taugy, &tauc); vector_sub(&tauc, &taugg, &tauc); //% commanded wheel torque, body axes //taub = tauh - tauc ; vector_sub(&tauh, &tauc, &taub); // Inject sine wave torque, body coordinates torque_injector(1, &taub); //% commanded wheel torque, wheel axes //tauw = CTRL.Ww_b * taub ; vector_clear(tauw); matrix_mac(tomatrix(3, 3, CTRL.Ww_b, &Ww_b), &taub, (MATRIX *)tauw); // Inject sine wave torque, wheel coordinates torque_injector(2, tauw);
  • 58. Living with this Code • The algorithms are the abstractions • Currently worked on by one engineer • Printfs for debugging • Code commented out when no longer used • Get it working, get it working in the lab (not simulations) • Leave well enough alone… – No refactoring
  • 59. Technical Debt “ It’s easy to accrue. It’s hard to payoff. What’s lacking is the enforcer from the loan shark. And the good business reasons you had for accruing it in the first place are likely still there when you have to decide if you are going to fix it or do something else. —Allen Wirfs-Brock, editor of the ECMAScript standard
  • 60. Living with the Ugly: A Story of Extending JavaScript How can you define new, compatible functions? array.fill()—set a span of elements starting somewhere with a single value array.transfer()—move a span of elements from one position to another position within the same array
  • 61. An Ugly Challenge • slice is an existing function that takes a start and an end-position argument. • splice is an existing function that takes a start and length argument. • indexOf is a function that returns either a position or -1.
  • 62. The Ugly Details: Nothing is Consistent, Lots of Defaults array.splice(startIndex, howManyToRemove, Optional elements to add…) Adds/removes items to/from an array, and returns the removed item(s) start index can be negative If negative, count back from the end of the array end index is optional array.indexof(searchItem, startIndex) start index can be negative start index is optional returns -1 if item not found array.slice(startIndex, endIndex) Returns an array starting at the given start argument, and ends at, but does not include, the given end argument Searches array for specified item and returns its position. start index can be negative If negative, count back from the end of the array How many to remove can be 0. If zero, add elements at start index.
  • 63. Fill fill(value, start=0, count=this.length-start) /* Every element of array from start up to but not including start+count is assigned value. Start and count are coerced to Number and truncated to integer values. Negative count is treated as if it was 0. Negative start values are converted to positive indices relative to the length of the array: if (start<0) start = this.length-start Reference to start and count below assume that conversion has already been applied If count==0 no elements are modified. If start+count>this.length and this.length is read-only a Range error is thrown and no elements are modified. If start+count>this.length and this.length is not read-only, this.length is set to start+count. Array elements are set sequentially starting with the start index. The array is returned as the value of this method */
  • 64. Examples aFloatArray.fill(Infinity); //Fill all elements of a Typed Array with a value. aFloatArray.fill(-1,6); //Fill all elements of a Typed Array starting at element 6 with -1. aFloatArray(1.5, 1, 5); //Fill the first 5 elements of a Typed Array. [ ].fill("abc",0,12).fill("xyz",-1,12); //Create a regular array, fill its first dozen elements with "abc", and its 2nd dozen elements with "xyz”
  • 65. Design Rationale • Placing the fill value first means no indices need to be specified when filling all elements. (Reasonable Defaults for optional arguments) • Follows start/count argument pattern similar to Array splice. (Compatible with some existing function) • I initially tried the slice argument style assuming that this would have more utility when combined with search functions that return array indices (indexOf, findIndex, etc). But they return -1 to indicate failure which isn't a good fit with the slice style. • The start+count model seems conceptually simpler to understand and specify.
  • 66. Transfer transfer(target=0,source=0, count=this.length-source) /* The sequence of array elements from source index up to but not including source+count are transferred within the array to the span of elements starting at the target index. The length of the array is not modified. The transfer takes into account the possibility that the source and target ranges overlap. source, target, and count are coerced to Number and truncated to integer values. Negative source and target indices are converted to positive indices relative to the length of the array. If count==0 no elements are modified. If source+count>this.length a Range error is thrown and no elements are modified. If target+count>this.length and this.length is read-only a Range error is thrown and no elements are modified. If target+count>this.length and this.length is not read-only, this.length is set to target+count. Array elements are sequentially transferred in a manner appropriate to avoid overlap conflicts. If target<=source a left to right transfer is performed. If target>source a right to left transfer is performed. If a target element is encountered that cannot be assigned, a type error is thrown and no additional elements are modified. Missing elements are processed as if they had the value undefined. Sparse array "holes" are transferred just like for other array functions. The array is returned as the value of this method */
  • 67. Examples [0,1,2,3,4].transfer(0,2); //[2,3,4,3,4] [0,1,2,3,4].transfer(2,0,2); //[0,1,0,1,4] [0,1,2].transfer(1); // [0,0,1,2] Int8Array.from([0,1,2]).transfer(1); //RangeError Int8Array.from([0,1,2]).transfer(1,0,2); // Int8Array 0,0,1 Int8Array.from([0,1,2]).transfer(0,1,2); // Int8Array 1,2,2
  • 68. Design Rationale • I considered "copy" and "move" names. Copy seems confusing. People might expect that anArray.copy(1,10) might create a new array, essentially meaning the same thing as Array.from(anArray). Move suggests a transfer that removes the source rather than replicating it. "transfer” doesn't carry those connotations. • See the fill rationale for why the slice argument patterns isn't used. • Defaulting count to this.length-source seems easiest to explain. There might be some utility in making the default count be min(this.length- source,this.length-target) but it could cause problems. • I considered a final optional "fill" parameter which would provide a value to put into each source element after it was copied (taking overlap into account). This would make it more like a true "move" operation. However, I could not convince myself that there was enough utility to justify the added complexity. (Do the simplest reasonable thing)
  • 70. “Now remember, things look bad and it looks like you’re not gonna make it, then you gotta get mean. I mean plumb, mad-dog mean. ‘Cause if you lose your head and you give up then you neither live nor win. That’s just the way it is.” -Clint Eastwood, The Outlaw Josey Wales
  • 71. Conclusions • Over time, code gets more complex • It’s easier to clean up as you go • Hard, not impossible, to clean up later • Don’t alwaysdo the “easiest” thing • Prepare to do the greater good • Make your goal ongoing sustainability

Editor's Notes

  1. Clint Eastwood as &quot;Blondie&quot;: The Good (a.k.a. the Man with No Name), a subdued, cocksure bounty hunter who teams with Tuco, and Angel Eyes temporarily, to find the buried gold.
  2. “An artist wants to make a sculpture, but before she makes the sculpture, she just massages the clay. She starts towards making the sculpture, and sees what the clay wants to do. And the more she handles the clay, the more the clay tends to do what she wants. It becomes compliant to her will. A development team works on a piece of code over several months. Initially, they make a piece of code, and it&apos;s a little stiff. It&apos;s small, but it&apos;s still stiff. Then they move the code, and it gets a little easier to move”
  3. So today, let&apos;s write a program simply. But let&apos;s also realize that tomorrow, we&apos;re going to make it more complex, because tomorrow it&apos;s going to do more. So we&apos;ll take that simplicity and we&apos;ll lose some of it. But tomorrow, hopefully tomorrow&apos;s program is as simple as possible for tomorrow&apos;s needs. Hopefully we&apos;ll preserve simplicity as the program grows.
  4. When you have both firmly under your belt, that’s real power…
  5. What should defaults be?Port 80The dilemma: Is default behavior always the right thing? Or a cop out?OK…if all else fails and you have no recovery possible (or you think this can never happen)….just swallow it and break the prime direct
  6. Lee Van Cleef “Angel Eyes”
  7. http://apprenticecoder.wordpress.com/2012/02/19/living-inside-moodle-e-learning-platform-source-code-and-db-structure/#more-1476
  8. http://www.brandonsavage.net/writing-intentionally-bad-code/Writes carefully constructed bad code that students rework….
  9. As I told you in our skype meeting, things should be visible to senior management, and they have to take decisions accordingly. If, for example, they decided to dedicate 5% of the team buffer for refactoring, and the team find that they will probably need 2 years to complete the next stage of refactoring. In this case, it is up the the manager to either increase the percentage of time dedicated for refactoring or do whatever other solution they find appropriate. In my case, the solution was to reduce the amount of code to work on. In stead of working on the whole 1.7 million lines of code, we chose to work on the core and most important one, whic his 500 thousands LOC only.  I am not sure what you mean by team buffer. I can guess, but is that the “extra time” allowed for work? If so, what % of time is minimal to do refactoring work, do you think? It can’t be too minimal or the team will not make enough progress.
  10. . They worked on reducing exact duplicates for two iterations  and managed to reduce code by another 20 KLOC.Comments and empty lines were not counted. Duplicate code is measured for level 1 (exact), or level 2 (changed variable names). Only un-gapped code clones are counted.
  11. Touco
  12. control of the telescope cannot be done by pushing off of anything. All motion must be controlled inertially: there are three &quot;reaction wheels&quot; -- 40 lb wheels that accelerate to change the momentum (equal and opposite reaction) of the telescope and point it at a new location or correct for wind shear.  Additionally, one 26 lb lead brick moves to adjust the balanceIt has an array of sensors on the telescope -- a magnetometer for gross pointing relative to Earth&apos;s field, an accelerometer, three cameras for determining position by the stars, and a gyroscope -- and another set on the fixed gondola -- GPS, accelerometer, magnetometer.  These are read at 10 Hz and processed through a Kalman filter with a 2 s memory (smoothing and looking for outliers due to noise) to determine pointing and motion, accounting for the reliability and accuracy of each instrument.  The pointing algorithm computes the necessary wheel torque to correct the pointing of the telescope, and the lead bricks move to zero the inertia of the entire telescope (including the reaction wheels).  If updated data miss the 10 Hz clock of the main pointing algorithm (sensors or actuators), the value from the preceding 0.1 s cycle is used.  The entire system communicates via a wireless/ethernet mix (no wires pass between the telescope and the balloon gondola).  The gondola carries the omin-directional antennas, which give us a 100 kB/s downlink but somewhere under 10 bytes/s (I think it is 2) uplink.InFOCuS continues to develop. X-Calibur, a new instrument that will detect the polarization of X-ray, will fly by 2014. And the InFOCuS team is investigating long-duration flights that would be launched from Antarctica, where wind patterns and 24-hours-a-day summer sunlight make the long flights possible. Thirty-day flights have occurred and a newly designed high-pressure balloon might stay aloft for more than one hundred days. That, notes Ramsey, could make them a low-cost competitor with orbital missions.A new pointing system that can keep balloon-based telescopes aimed at distant objects with unprecedented accuracy should also contribute to the scientific value of future missions. The Wallops Arc Second Pointer (or WASP), developed at NASA’s Wallops Flight Facility, can steadily aim a telescope at an object or area a single arc-second wide. Ramsey hopes that WASP will be used on a future “Super-HERO” mission.
  13. Going with the flow can make things worseBut sometimes it is the only reasonable thing to doIt’s easier to clean up as you goBut not impossible to clean up later (just much, much harder)Challenge yourself and your team