Contact Info Thanks:
Contact Info Thanks:
I've seen a lot of code.
var o=n=>n+(!/1.$/.test(--n)&&'snr'[n%=10]+'tdd'[n]||'th')
I want to start with some code.
I'll giveyou all about 1:00.
Raise your hand when you think you understand what this code does.
function getOrdinalForNumber(number) { var suffix = getSuffix(number); return `${number}${suffix}`;}function getSuffix(number) { if (numberEndsInElevenTwelveOrThirteen(number)) { return "th"; } var lastDigit = number % 10; switch (lastDigit) { case 1: return "st"; case 2: return "nd"; case 3: return "rd"; default: return "th"; }}function numberEndsInElevenTwelveOrThirteen(number) { var asString = number.toString(); return ( asString.endsWith("11") || asString.endsWith("12") || asString.endsWith("13") );}
Do it again.
getOrdinalForNumber(3);
3rd
Both of them do the same thing.
...
But we aren't here to play code golf.
We're here to talk about ....
Which I will say in front of you all, is pretty funny,
because as someone who is extremely uncomfortable in social situations,
I would say that my general advice about communication with others is
I am a pretty active guy,
I usually prefer to go for a run or bike ride by myself than to talk to people.
...
but I know a person.
And so here's another introduction slide.
I lean on her a lot in social situations
because she's great at communicating.
So I had the idea for this talk
And I said Steph,
I have this idea, but I kind of stink at communication.
And she said
for starters...
And at first I got mad
Because I tend to take this kind of feedback personally.
But then I started to think about it.
When I write code, I try to be direct.
I try to follow the SRP
I try to follow it with classes
And I try to follow it with functions
function loadSomeData(){ me.loading = true; var dataRequest = {}; dataRequest.ids = _.pluck(me.items, 'id'); dataRequest.startDate = formatDate(me.startDate); dataRequest.endDate = formatDate(me.endDate); dataService.loadData( dataRequest, function (result) { me.fieldA = result.fieldA; me.fieldB = result.fieldB; notify('Data loaded!'); } );}
If my function here does several things,
I'll try to identify them
(identify!)
function loadSomeData(){ me.loading = true; var dataRequest = {}; dataRequest.ids = _.pluck(me.items, 'id'); dataRequest.startDate = formatDate(me.startDate); dataRequest.endDate = formatDate(me.endDate); dataService.loadData( dataRequest, function (result) { me.fieldA = result.fieldA; me.fieldB = result.fieldB; notify('Data loaded!'); } );}
function loadSomeData(){ me.loading = true; var dataRequest = {}; dataRequest.ids = _.pluck(me.items, 'id'); dataRequest.startDate = formatDate(me.startDate); dataRequest.endDate = formatDate(me.endDate); dataService.loadData( dataRequest, function (result) { me.fieldA = result.fieldA; me.fieldB = result.fieldB; notify('Data loaded!'); } );}
function loadSomeData(){ me.loading = true; var dataRequest = {}; dataRequest.ids = _.pluck(me.items, 'id'); dataRequest.startDate = formatDate(me.startDate); dataRequest.endDate = formatDate(me.endDate); dataService.loadData( dataRequest, function (result) { me.fieldA = result.fieldA; me.fieldB = result.fieldB; notify('Data loaded!'); } );}
function loadSomeData(){ me.loading = true; var dataRequest = {}; dataRequest.ids = _.pluck(me.items, 'id'); dataRequest.startDate = formatDate(me.startDate); dataRequest.endDate = formatDate(me.endDate); dataService.loadData( dataRequest, function (result) { me.fieldA = result.fieldA; me.fieldB = result.fieldB; notify('Data loaded!'); } );}
function loadSomeData(){ setLoadingState(); var dataRequest = buildDataRequest(); dataService.loadData( dataRequest, handleResponse );}
function setLoadingState(){ me.loading = true;}function buildDataRequest(){ //...}function handleResponse(data){ //...}
and extract them to more focused methods,
So I can read through them one abstraction at a time.
I try to write cohesive code
I try to group related elements.
...
public Player GetPlayer()public void AddPlayer(Player player)public void UpdatePlayer(Player player)public Team GetTeam()public void AddTeam(Team team)public void UpdateTeam(Team team)
I try to group related elements.
...
This helps me identify patterns and repetition easily
And find areas of code that might be suitable for extracting methods or classes.
I prefer to organize my code by feature, rather than type.
...
app/ App.js actions/ calendar.js speakers.js user-profile.js containers/ CalendarContainer.js SpeakerListContainer.js UserProfileContainer.js components/ Calendar.js Session.js Speaker.js SpeakerList.js UserProfile.js reducers/ root.js calendar.js speakers.js user-profile.js
I prefer to organize my code by feature, rather than type.
...
On the left, is a pretty standard react app.
I have my code organized by object type.
The problem with this approach
is that when my system changes, it's not all the containers that change.
app/ App.js actions/ calendar.js speakers.js user-profile.js containers/ CalendarContainer.js SpeakerListContainer.js UserProfileContainer.js components/ Calendar.js Session.js Speaker.js SpeakerList.js UserProfile.js reducers/ root.js calendar.js speakers.js user-profile.js
It's "something to do with speakers".
I need to bounce around all these folders to find all of the speaker files.
app/ App.js actions/ calendar.js speakers.js user-profile.js containers/ CalendarContainer.js SpeakerListContainer.js UserProfileContainer.js components/ Calendar.js Session.js Speaker.js SpeakerList.js UserProfile.js reducers/ root.js calendar.js speakers.js user-profile.js
app/ App.js calendar/ calendar.actions.js calendar.reducer.js Calendar.container.js Calendar.component.js Session.component.js speakers/ speakers.actions.js speakers.reducer.js SpeakerList.container.js SpeakerList.component.js Speaker.component.js attendees/ user-profile.actions.js user-profile.reducer.js UserProfile.container.js UserProfile.component.js
When I organize by feature,
All of my speaker things are in one folder.
This structure, to me, is more cohesive.
we have a history of putting things in buckets that are based on horizontal slices
but it's the vertical slices - the features - that put "code that changes together" in the same place.
this is applicable on both server side and client
there are variations of this slide that have been used to argue for technologies like css-in-js
but it is appropriate everywhere.
...
It's kind of like packing your clothes.
Underwear, pants, shirts, all separated, it's kind of a mess when you want to get dressed the next day.
I've never tried it...but what if we all just started packing by outfits instead?
That way we just have to grab a whole outfit and go. No digging.
(if we changed our clothes, anyway)
Another way to be direct with code
is to be explicit, rather than implicit.
When I have a mathematical expression,
I am explicit with parentheses,
so that the reader doesn't have to remember the order of operations.
...
var overallIndex = pageSize * pageNumber + indexOnPage;
When I have a mathematical expression,
I am explicit with parentheses,
so that the reader doesn't have to remember the order of operations.
...
If I look at this statement for a bit, I can
deduce the order of operations.
...
var overallIndex = pageSize * pageNumber + indexOnPage;
var overallIndex = (pageSize * pageNumber) + indexOnPage;
When I have a mathematical expression,
I am explicit with parentheses,
so that the reader doesn't have to remember the order of operations.
...
If I look at this statement for a bit, I can
deduce the order of operations.
...
Or, I can throw parentheses in so that
I don't have to spend any time at all identifying the OOO.
...
So I look back at my wife and I say
You're right. I can be more direct.
What else you got for me?
And she says
...
And I realized, I totally get this
I've got kids.
I want to introduce you to one of them.
You've been seeing photos of my daughters Lila and Olivia.
But I want to talk specifically about...
Olivia.
This is Supercat. (sidetrack)
Olivia will do anything for a laugh.
But she's not great at making decisions.
and so I have this conversation daily.
Daaad! I'm hungry!
Daaad! I'm hungry!
Okay, Liv. Want some yogurt?
Daaad! I'm hungry!
Okay, Liv. Want some yogurt?
No! I hate yogurt!
Daaad! I'm hungry!
Okay, Liv. Want some yogurt?
No! I hate yogurt!
Okay. How about an apple?
Daaad! I'm hungry!
Okay, Liv. Want some yogurt?
No! I hate yogurt!
Okay. How about an apple?
Dad I said I wanted yogurt!!!!
Olivia has trouble communicating clearly what she wants
So I understand the importance of clarity.
And I feel pretty strongly about the importance of being clear in my code
...
especially when it comes to naming things.
Naming is the hardest thing in development.
Here are some rules I like to follow.
function calculateOnBasePercentage( a, b, c, d, e){ var top = a + b + c; var bottom = b + c + d + e; return top / bottom}
On the left, you see code where the variables don’t really mean anything.
And in fact, in order to know what the variables mean,
I need to know the formula for obp.
And this is the opposite of how it should be.
...
function calculateOnBasePercentage( a, b, c, d, e){ var top = a + b + c; var bottom = b + c + d + e; return top / bottom}
function calculateOnBasePercentage( hits, walks, hitByPitch, atBats, sacrificeFlies){ var top = hits + walks + hitByPitch; var bottom = walks + hitByPitch + atBats + sacrificeFlies; return top / bottom}
On the left, you see code where the variables don’t really mean anything.
And in fact, in order to know what the variables mean,
I need to know the formula for obp.
And this is the opposite of how it should be.
...
On the right, the variable names mean something,
making it easier to tell what the function is doing.
When you don't name with intention,
you force readers to have to think.
If the function does multiple things, all of those things should be identified in the name.
If there are side effects, the name should mention that.
public void _____(ShoppingCart cart){ this.ShoppingCartRepository.Add(cart); this.ClearCart();}
If the function does multiple things, all of those things should be identified in the name.
If there are side effects, the name should mention that.
public void _____(ShoppingCart cart){ this.ShoppingCartRepository.Add(cart); this.ClearCart();}
public void SaveCart(...)// Doesn't tell the whole story
If the function does multiple things, all of those things should be identified in the name.
If there are side effects, the name should mention that.
public void _____(ShoppingCart cart){ this.ShoppingCartRepository.Add(cart); this.ClearCart();}
public void SaveCart(...)// Doesn't tell the whole story
public void SaveAndClearCart(...)// Tells the whole story
If the function does multiple things, all of those things should be identified in the name.
If there are side effects, the name should mention that.
...
And you'll notice that the function name got a little bit longer,
but...
As developers, we spend a lot of time trying to make things small.
The length of a name should not be one of those things.
...
//ambiguous namefunction process(request) {}
As developers, we spend a lot of time trying to make things small.
The length of a name should not be one of those things.
...
Short names are generally ambiguous.
...
//ambiguous namefunction process(request) {}
//unambiguous namefunction saveUserProfile(request) {}
As developers, we spend a lot of time trying to make things small.
The length of a name should not be one of those things.
...
Short names are generally ambiguous.
...
Longer names are generally more clear.
...
Encoded names are also unclear.
As a general rule, if you can’t pronounce the name out loud, it is probably encoded.
...
//encoded names//hungarian notationvar dblPrice;//abbreviationsvar flNmWoExt;//scopingvar m_Title;
Encoded names are also unclear.
As a general rule, if you can’t pronounce the name out loud, it is probably encoded.
...
Some examples of encoding are
Hungarian notation,
abbreviations,
and scoping prefixes.
...
//encoded names//hungarian notationvar dblPrice;//abbreviationsvar flNmWoExt;//scopingvar m_Title;
//not encodedvar price;var fileNameWithoutExtension;var title;
Encoded names are also unclear.
As a general rule, if you can’t pronounce the name out loud, it is probably encoded.
...
Some examples of encoding are
Hungarian notation,
abbreviations,
and scoping prefixes.
...
Encoding forces the reader to
decipher code as it’s read.
Sometimes we forget to name things altogether.
magic numbers and strings are easier to understand
when they are constants with a descriptive name
return seconds / 86400;
Sometimes we forget to name things altogether.
magic numbers and strings are easier to understand
when they are constants with a descriptive name
...
who can tell me what this number represents?
return seconds / 86400;
const secondsInADay = 86400;return seconds / secondsInADay;
Sometimes we forget to name things altogether.
magic numbers and strings are easier to understand
when they are constants with a descriptive name
...
who can tell me what this number represents?
...
who can tell me now?
...
There's something else I think about when I think about being clear with my code.
and that's comments.
This tweet summarizes a lot of what I think about comments.
Your IDE doesn’t keep comments up to date like it does variable names.
And chances are, you don’t either.
Once you understand the code, you stop reading the comments.
function isHallOfFameWorthy(player){ //a player is considered HOF worthy if // he played for 18 seasons //<------- 18 return player.seasonsPlayed > 20; //<------- 20}
In this example, you can see that a comment says one thing, but the code says another.
Comments don’t excuse confusing code, which is too often how we use them.
...
function inductPlayer(player) { //induct a player if he is awesome. // a player is considered awesome if // he played for the brewers, // won a world series, // or was named steve. if (player.teams['brewers'] || player.worldSeriesTitleCount > 0 || player.firstName == 'Steve') { hallOfFame.induct(player); }}
Comments don’t excuse confusing code, which is too often how we use them.
...
We write code that isn’t clear, and then we write lengthy comments summarizing the code.
...
function inductPlayer(player) { //induct a player if he is awesome. // a player is considered awesome if // he played for the brewers, // won a world series, // or was named steve. if (player.teams['brewers'] || player.worldSeriesTitleCount > 0 || player.firstName == 'Steve') { hallOfFame.induct(player); }}
function inductPlayer(player) { if (isAwesome(player)) { hallOfFame.induct(player); }}
Comments don’t excuse confusing code, which is too often how we use them.
...
We write code that isn’t clear, and then we write lengthy comments summarizing the code.
...
But instead of 5 lines of comments and 3 lines of code, wouldn’t you rather see one line of code?
// ________________// | |_____ __// | I'm Sorry | |__| |_________// |________________| |::| | /// /\**/\ | \.____|::|__| <// ( o_o )_ | \::/ \._______\// (u--u \_) |// (||___ )==\// ,dP"/b/=( /P"/b\// |8 || 8\=== || 8// `b, ,P `b, ,P// """` """`
Comments are often an apology
For not being able to express ourselves in the code clearly.
If you are apologizing, you'll want to include the "i'm sorry" kitten on a bicycle
so i'll leave that here for you.
Here's another thing I think about comments
I remember being taught in college to comment every line.
public void SavePlayer(){ //open the file this.OpenFile(); var player = { //the name of the player Name = "Jim Gantner", //the player's position Position = "2B" } //save the player to file this.SavePlayerToFile(player);}
I remember being taught in college to comment every line.
These comments just echo the code.
They are just noise.
If I'm writing code that is clear, I don't need them.
as in unused code,
a scenario which is better served by source control.
function isAwesome(player) { //return player.yearsExperience > 8 //return player.yearsExperience > 15 //return player.yearsExperience > 12 return player.yearsExperience > 10 && player.hits > 3000 && player.ops > .850;}
as in unused code,
a scenario which is better served by source control.
This isn't to say all comments are bad.
The right comment in the right situation can be helpful
function mapPlayer(players) { //in: // [ // { // playerId: 1, // name: 'Jim Gantner', // position: '2B' // }, // ... // ] //out: // [ // 'Jim Gantner' // ] return _.chain(players) .where({ position: '2B' }) .pluck('name') .value();}
In this case, telling me the expected input and output of this function.
These types of comments are especially useful when doing
functional-style filter, map, reduce operations
where you're mapping things from one shape to another
Comments can also simplify
something like a regex
...
function validateTime(time) { //valid formats: HH:MM, // where HH is in 24 hour format var regex = new RegExp( "^([0-1][0-9]|[2][0-3]):([0-5][0-9])$"); return regex.test(time);}
Comments can also simplify
something like a regex
...
It takes time to decipher a regular expression.
A comment can help you understand the code faster.
With the caveat that if you aren't keeping it up to date,
it's not doing any good.
advise the reader why an unexpected line needs to be there.
function getMiddleInfielders(players) { return _.filter(players, function(player) { return player.position == '2B' || player.position == 'SS' // we have to include third basemen, // because sometimes the shift is on. || player.position == '3B' });}
advise the reader why an unexpected line needs to be there.
...
So after all of this thinking about how to make my code more clear,
I start to get excited
and I ramble a whole bunch about names and comments and regular expressions and she says
whoa whoa whoa, slow down.
Next rule for good communication.
And again, I was a little hurt by this
...
But then I started to bring it back to code again.
I can think of some ways my code is concise,
especially when it comes to functions
I have this theory that...
Functions should read less like war & peace
And more like a choose your own adventure book
And we can accomplish this with small units of code.
function loadSomeData(){ me.loading = true; var dataRequest = {}; dataRequest.ids = _.pluck(me.items, 'id'); dataRequest.startDate = formatDate(me.startDate); dataRequest.endDate = formatDate(me.endDate); dataService.loadData( dataRequest, function (result) { me.fieldA = result.fieldA; me.fieldB = result.fieldB; notify('Data loaded!'); } );}
And we can accomplish this with small units of code.
This is my example from before
Where I identified all of the different things happening
And extracted them
...
function loadSomeData(){ me.loading = true; var dataRequest = {}; dataRequest.ids = _.pluck(me.items, 'id'); dataRequest.startDate = formatDate(me.startDate); dataRequest.endDate = formatDate(me.endDate); dataService.loadData( dataRequest, function (result) { me.fieldA = result.fieldA; me.fieldB = result.fieldB; notify('Data loaded!'); } );}
function loadSomeData(){ setLoadingState(); var dataRequest = buildDataRequest(); dataService.loadData( dataRequest, handleResponse );}
function setLoadingState(){ me.loading = true;}function buildDataRequest(){ //...}function handleResponse(data){ //...}
And we can accomplish this with small units of code.
This is my example from before
Where I identified all of the different things happening
And extracted them
...
I want to read a small excerpt, and
from its contents, identify where I need to read next.
Through this process, I can understand the code one small bit at a time.
Here's a hint that your units of code are too large -
You have a lot of indentation.
function searchPlayers(team, position, searchText) { var results = []; players.forEach(function(player) { if (player.team == team) { player.positions.forEach(function(playerPosition) { if (playerPosition == position) { if (player.firstName.startsWith(searchText)) { results.push({ playerId: player.playerId, fullname: `${player.firstName} ${player.lastName}` }); } else if (player.lastName.startsWith(searchText)) { results.push({ playerId: player.playerId, fullname: `${player.firstName} ${player.lastName}` }); } } }); } }); return results;}
Here's a hint that your units of code are too large -
You have a lot of indentation.
...
Indented code is confusing.
function searchPlayers(team, position, searchText) { return players .filter(p => p.team == team) .filter(p => positionMatches(p, position)) .filter(p => playerNameMatches(p, searchText)) .map(mapMatchingPlayers);}function positionMatches(player, position) { return player.positions .filter(pos => pos == position).length > 0;}function playerNameMatches(player, searchText) { return ( player.firstName.startsWith(searchText) || player.lastName.startsWith(searchText) );}function mapMatchingPlayers(player) { return { playerId: player.playerId, fullname: `${player.firstName} ${player.lastName}` };}
When possible, you can avoid it by extracting functions.
Another way to identify if your function could be more concise
is if it has regions
...
public void GetCurrentGameScenario( RequestModel request){#region Get Player Info var playerFromDb = GetPlayerById( request.PlayerId); var playerResult = MapPlayer(playerFromDb);#endregion#region Get Game Info var gameFromDb = GetGameById( request.GameId); var gameResult = MapGame(gameFromDb);#endregion}
Another way to identify if your function could be more concise
is if it has regions
...
In this example, you can see I’ve got sections of code indicated by regions.
...
public void GetCurrentGameScenario( RequestModel request){#region Get Player Info var playerFromDb = GetPlayerById( request.PlayerId); var playerResult = MapPlayer(playerFromDb);#endregion#region Get Game Info var gameFromDb = GetGameById( request.GameId); var gameResult = MapGame(gameFromDb);#endregion}
public void GetCurrentGameScenario( RequestModel request){ var playerResult = GetPlayerInfo( request.PlayerId); var gameResult = GetGameInfo( request.GameId);}
public PlayerViewModel GetPlayerInfo( int playerId){ //...}public GameViewModel GetGameInfo( int gameId){ //...}
Another way to identify if your function could be more concise
is if it has regions
...
In this example, you can see I’ve got sections of code indicated by regions.
...
If I extract those blocks into separate methods,
my top level method becomes a nice concise outline.
...
This rule for regions also extends to classes.
If you can find commonality between a bunch of functions in a class,
enough to create a region,
that region could probably be its own class.
The more arguments you have,
the more mental mapping the reader has to do,
to keep the sequence straight.
...
function buildTeam( sp, c, _1b, _2b, _3b, ss, lf, cf, rf) { //... }}
The more arguments you have,
the more mental mapping the reader has to do,
to keep the sequence straight.
...
There are no hard limits, but fewer is better, 3 is a lot, and that is way too many.
There are a couple of ways out of this.
First, we can look at our method and decide if it is doing too much.
If so, break it up.
...
function buildTeam( sp, c, _1b, _2b, _3b, ss, lf, cf, rf) { //... }}
function buildTeam( battery, infield, outfield) { //... }}
The more arguments you have,
the more mental mapping the reader has to do,
to keep the sequence straight.
...
There are no hard limits, but fewer is better, 3 is a lot, and that is way too many.
There are a couple of ways out of this.
First, we can look at our method and decide if it is doing too much.
If so, break it up.
...
Second, it's possible that the arguments you're passing
are conceptually related.
In this case, you can combine arguments into objects.
(w,g,c,m)=>(b=(`G`[r=`repeat`](w)+``)[r](g))+(`C`[r](w)+``)[r](c)+(`M`[r](w)+``)[r](m)+b
there is a balancing act between being concise and being complete.
you don't want to be so concise that you've written code golf
But just like communicating verbally
Unnecessary words are noise.
...
So after all of this thinking about how to make my code more concise, I told my wife
Yeah. I recognize the value of being concise.
What did she have for me next?
She said,
When you repeat yourself, I stop listening.
And this made me kind of mad...
But again I brought it back in my head to code.
One of my favorite rules to follow is the
Repetition in code is a bug in waiting.
You change things in 4 places, but forget about the 5th.
One of the best ways to eliminate duplication is by extracting methods.
...
function loadPlayerStats(players) { var _1b = players['cecil cooper']; var _1bStatsRequest = { playerId: _1b.id }; statsService .loadStats(_1bStatsRequest) .then(function (result){ _1b.stats = result; }); var _2b = players['jim gantner']; var _2bStatsRequest = { playerId: _2b.id }; statsService .loadStats(_2bStatsRequest) .then(function (result){ _2b.stats = result; }); var _3b = players['paul molitor']; var _3bStatsRequest = { playerId: _3b.id }; statsService .loadStats(_3bStatsRequest) .then(function (result){ _3b.stats = result; });}
One of the best ways to eliminate duplication is by extracting methods.
...
Here, you can see I’m repeating about 9 lines of code for several different players.
function loadPlayerStats(players) { var _1b = players['cecil cooper']; var _1bStatsRequest = { playerId: _1b.id }; statsService .loadStats(_1bStatsRequest) .then(function (result){ _1b.stats = result; }); var _2b = players['jim gantner']; var _2bStatsRequest = { playerId: _2b.id }; statsService .loadStats(_2bStatsRequest) .then(function (result){ _2b.stats = result; }); var _3b = players['paul molitor']; var _3bStatsRequest = { playerId: _3b.id }; statsService .loadStats(_3bStatsRequest) .then(function (result){ _3b.stats = result; });}
One thing that's interesting about repeated code is often you can see it in the shape of the code.
function loadPlayerStats(players) { var _1b = players['cecil cooper']; var _1bStatsRequest = { playerId: _1b.id }; statsService .loadStats(_1bStatsRequest) .then(function (result){ _1b.stats = result; }); var _2b = players['jim gantner']; var _2bStatsRequest = { playerId: _2b.id }; statsService .loadStats(_2bStatsRequest) .then(function (result){ _2b.stats = result; }); var _3b = players['paul molitor']; var _3bStatsRequest = { playerId: _3b.id }; statsService .loadStats(_3bStatsRequest) .then(function (result){ _3b.stats = result; });}
One thing that's interesting about repeated code is often you can see it in the shape of the code.
function loadPlayerStats(players) { var _1b = players['cecil cooper']; var _1bStatsRequest = { playerId: _1b.id }; statsService .loadStats(_1bStatsRequest) .then(function (result){ _1b.stats = result; }); var _2b = players['jim gantner']; var _2bStatsRequest = { playerId: _2b.id }; statsService .loadStats(_2bStatsRequest) .then(function (result){ _2b.stats = result; }); var _3b = players['paul molitor']; var _3bStatsRequest = { playerId: _3b.id }; statsService .loadStats(_3bStatsRequest) .then(function (result){ _3b.stats = result; });}
function loadPlayerStats(players) { var _1b = players['cecil cooper']; loadStatsForPlayer(_1b); var _2b = players['jim gantner']; loadStatsForPlayer(_2b); var _3b = players['paul molitor']; loadStatsForPlayer(_3b);};
function loadStatsForPlayer(player) { var statsRequest = { playerId: player.id }; statsService .loadStats(statsRequest) .then(function (result){ player.stats = result; });};
On the right, I’ve extracted the duplication into one method, improving readability.
And now I've only got one place that I need to change, instead of many.
Dry is a recommendation, not a hard rule.
When I first learned about DRY, everything seemed like duplication.
Now I find myself asking "is this duplication"?
What's the reason for these things changing?
If it seems like they would all change together, then yes, it's repetition.
If they might change for different reasons,
they might just be similar code.
This is an important distinction because you don't want to end up with the wrong abstraction.
So again, I said to my wife,
I get it. I can stop repeating myself.
No matter how many times I tell you about the amazing dream I had last night....
you don't care.
And she said exactly.
And we moved on to the next lesson.
Sometimes you talk too fast, and I can't keep up.
I had to remind myself of some coping strategies I've learned
(from booklets my daughter has brought home from school)
...
About the pacing -
I know I talk fast when I am trying to make a point
So I think I see where she's coming from
And then I thought about the ways I pace my code,
to make it more readable.
The first thing I like to do for pacing is use whitespace effectively
I find that vertical whitespace helps keep concepts separate, and therefore more readable.
...
function loadStatisticsForPlayers() { me.loading = true; var statsRequest = buildStatisticsRequest(); playerService.loadStatistics( statisticsRequest, onStatisticsLoaded);}
I find that vertical whitespace helps keep concepts separate, and therefore more readable.
...
On the left, I have no vertical whitespace, so my code appears cluttered.
...
function loadStatisticsForPlayers() { me.loading = true; var statsRequest = buildStatisticsRequest(); playerService.loadStatistics( statisticsRequest, onStatisticsLoaded);}
function loadStatisticsForPlayers() { me.loading = true; var statsRequest = buildStatisticsRequest(); playerService.loadStatistics( statisticsRequest, onStatisticsLoaded);}
I find that vertical whitespace helps keep concepts separate, and therefore more readable.
...
On the left, I have no vertical whitespace, so my code appears cluttered.
...
On the right, I’ve separated concepts with vertical whitespace.
This allows me to quickly identify the different things happening in the method.
Another way we can use vertical spacing is to give concepts their own line.
...
function getSecondBaseman(players) { return _.chain(players).where({ position: '2B'}).pluck('lastname').value();}
Another way we can use vertical spacing is to give concepts their own line.
...
Here again, I’ve got a line of code that chains together several clauses, and it reads like a runon sentence.
...
function getSecondBaseman(players) { return _.chain(players).where({ position: '2B'}).pluck('lastname').value();}
function getSecondBaseman(players) { return _.chain(players) .where({ position: '2B'}) .pluck('lastname') .value();}
Another way we can use vertical spacing is to give concepts their own line.
...
Here again, I’ve got a line of code that chains together several clauses, and it reads like a runon sentence.
...
By giving each clause its own line, I can more easily identify important concepts.
Aside from readability, another great reason to reduce the concepts per line
is how easy it is to see your changes in Git,
or whatever other source control you're using.
Aside from readability, another great reason to reduce the concepts per line
is how easy it is to see your changes in Git,
or whatever other source control you're using.
Aside from readability, another great reason to reduce the concepts per line
is how easy it is to see your changes in Git,
or whatever other source control you're using.
So at this point,
I got really excited and started telling her all about
whitespace and
Git and
beep boop beep boop
And she said UGH, this is what I'm talking about.
...
Sometimes it just seems like you are speaking a different language.
If you want to communicate with me, speak my language.
And this made me sad
But I realized, using the right language is also
one of my rules for writing readable code.
Especially when it comes to naming things
It's important to use appropriate language constructs.
...
var stats = statisticsForPlayers;
Especially when it comes to naming things
It's important to use appropriate language constructs.
...
This code seems all fantastic.
Just assigning one variable to another.
...
var stats = statisticsForPlayers;
function statisticsForPlayers() { me.loading = true; var statsRequest = buildStatisticsRequest(); playerService.loadStatistics( statisticsRequest, onStatisticsLoaded);}
Especially when it comes to naming things
It's important to use appropriate language constructs.
...
This code seems all fantastic.
Just assigning one variable to another.
...
Until you discover that statisticsForPlayers is a method, not an object.
Code is easier to read when you
use nouns for classes,
and verbs for functions.
When you use the wrong language constructs,
you have to think harder about what something is.
Misspelled names are confusing
When you mis-spell a word, you are in the minority
(except accommodation. no one knows how to spell that. we should get rid of it.)
Everything else...You can be certain that a majority of those reading your code can spell correctly.
...
function geenrateRandomNumber(){}
Misspelled names are confusing
When you mis-spell a word, you are in the minority
(except accommodation. no one knows how to spell that. we should get rid of it.)
Everything else...You can be certain that a majority of those reading your code can spell correctly.
...
So when I misspell the word generate,
whether it's a typo or I think that's how it's spelled,
It makes things harder for others to read.
...
function geenrateRandomNumber(){}
function generateRandomNumber(){}
Misspelled names are confusing
When you mis-spell a word, you are in the minority
(except accommodation. no one knows how to spell that. we should get rid of it.)
Everything else...You can be certain that a majority of those reading your code can spell correctly.
...
So when I misspell the word generate,
whether it's a typo or I think that's how it's spelled,
It makes things harder for others to read.
...
It also has the unfortunate side-effect
of preventing my IDE from auto-completing when I type "gen".
Inconsistent language also forces the reader to think when they read your code.
...
//inconsistent names for listsvar playerList;var teams;var listOfSeasons;
Inconsistent language also forces the reader to think when they read your code.
...
If I am inconsistent with naming lists of things,
I spend more time trying to identify what something is.
...
//inconsistent names for listsvar playerList;var teams;var listOfSeasons;
//consistent names for listsvar playerList;var teamList;var seasonList;
Inconsistent language also forces the reader to think when they read your code.
...
If I am inconsistent with naming lists of things,
I spend more time trying to identify what something is.
...
When you use consistent language,
reading your code requires less thought.
In this case, I can quickly identify if something is a list of things.
There's also something to be said for symmetry in naming
...
//asymmetrical namespublic void FileOpen(...)public void CloseFile(...)
There's also something to be said for symmetry in naming
...
Asymmetry can show itself a couple ways.
1- Ordering of terms in the name
We're naturally drawn to symmetry so this throws us off.
...
//asymmetrical namespublic void FileOpen(...)public void CloseFile(...)
//asymmetrical namespublic void UnpackFile(...)public void CloseFile(...)
There's also something to be said for symmetry in naming
...
Asymmetry can show itself a couple ways.
1- Ordering of terms in the name
...
2- unnatural opposites
There is a specific book I remember reading to my kids
A tab-flappy book, and on the front of each flap was one word,
behind the flap was the opposite.
Up, down. In, out. Open, Close.
These words are natural opposites, and we understand them from a very young age.
...
//asymmetrical namespublic void FileOpen(...)public void CloseFile(...)
//asymmetrical namespublic void UnpackFile(...)public void CloseFile(...)
//symmetrical namespublic void OpenFile(...)public void CloseFile(...)
There's also something to be said for symmetry in naming
...
Asymmetry can show itself a couple ways.
1- Ordering of terms in the name
...
2- unnatural opposites
There is a specific book I remember reading to my kids
A tab-flappy book, and on the front of each flap was one word,
behind the flap was the opposite.
Up, down. In, out. Open, Close.
These words are natural opposites, and we understand them from a very young age.
...
Symmetrical names are easier to read
because you understand them without thinking.
And the idea of unnatural opposites
brings me to metaphors.
Metaphors are high-risk high-reward.
A good metaphor can be really helpful for readability.
It provides the reader a shortcut to understanding the code.
But you really have to nail the metaphor.
...
// cuz metronomes "keep time".public interface IMetronome { DateTime Now { get; }}
And the idea of unnatural opposites
brings me to metaphors.
Metaphors are high-risk high-reward.
A good metaphor can be really helpful for readability.
It provides the reader a shortcut to understanding the code.
But you really have to nail the metaphor.
...
If you use one that isn't quite right, it can make things more confusing.
And in this case, you can tell it's not right, because I have to add a cute comment.
I got too cute with this.
...
// cuz metronomes "keep time".public interface IMetronome { DateTime Now { get; }}
public interface IClock { DateTime Now { get; }}
And the idea of unnatural opposites
brings me to metaphors.
Metaphors are high-risk high-reward.
A good metaphor can be really helpful for readability.
It provides the reader a shortcut to understanding the code.
But you really have to nail the metaphor.
...
If you use one that isn't quite right, it can make things more confusing.
And in this case, you can tell it's not right, because I have to add a cute comment.
I got too cute with this.
...
My Clock interface doesn't need a cute comment
because it is the right metaphor.
I get it, I told her.
And we looked back at the list of things we'd talked about
To be better at communication
(read em)
And looking at this, and comparing it to all the ways I try to do all these things in code,
I said think I can sum it all up by saying, the key to good communication is...
Above all else, Don't make me think!
And she said "exactly!"
...
And this is the most important thing for code, too.
Code should not make the reader think.
When you're writing or reading code,
you're building a mental model in your head.
a lego spaceship in your head
It takes a lot of thought and effort to build this model up
, and even more to keep from losing it.
...
The more we have to think about the code we're reading,
the less effort we are able to put toward maintaining that model.
...
Development is already Hard
Don't Make it harder on yourself, and your teammates,
with code that communicates poorly.
Keyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
Number + Return | Go to specific slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
Esc | Back to slideshow |