At the end of that talk and blog post I present the “Stupidity Manifesto”, which I’m reproducing below so that you can sign it and encourage others to sign it too!
The Stupidity Manifesto
LET’S STOP MAKING EACH OTHER FEEL STUPID. Instead, let’s…
ENCOURAGE EVERYONE TO ASK QUESTIONS
Lead by example: Be honest when we’re confused
Value curiosity over knowledge
Prioritise clarity over jargon
Remember we all forget stuff
Get excited about teaching and learning
Acknowledge the broad range of knowledge in our industry, and avoid judging someone if their knowledge doesn’t match ours
LET’S STOP MAKING EACH OTHER FEEL STUPID.
To sign the manifesto, simply add your support as a comment on this post. Each comment will be counted as another signature in favour of the manifesto.
To sign the manifesto, simply add your support as a comment on this post. Each comment will be counted as another signature in favour of the manifesto.
Once upon a time, there was a female software engineer. When she studied mathematics at university, she was one of a tiny minority of female students. That was the first time she internalised the idea that women can’t do the same things that men can do. The evidence was in front of her eyes. And it was there again when she switched to software engineering. In her first job, she was the only female developer. And compared to her colleagues, she was a late developer. It seemed they had all taught themselves to code as children and teenagers. But this was the mid 90s. She attended university in the early 90s, where she never owned a computer and barely used one until after she graduated.
A few years later, in her second software job, she kept asking to work on the more difficult, more interesting, code bases… but she was always denied. When she interviewed for a more senior position elsewhere, she was told she didn’t know enough. She wasn’t surprised. It confirmed all her worst suspicions about herself.
Twelve years into her career, she was laid off and it was a relief. She had never felt like she belonged. She didn’t want to work in IT any more.
Here’s a question for you: Do you ever have to Google anything related to your sphere of expertise? When you do, do you shame yourself for it? Do you feel as though you’re supposed to know everything, and there’s something wrong with you if you don’t?
If you feel this way, you’re not alone. The woman described above is me. But now I’ve been doing this for over 23 years, and I make a living out of teaching software engineering skills. Does this mean I’ve finally reached the “comfortably confident that I know all the things” stage of my career? Nope. Quite the opposite.
The more I learn, the better I understand how little I know.
Being an effective IT professional is not about what you know!
These days, when I find myself beating myself up for my ignorance (and I’m sorry to tell you that I do still beat myself up), I remind myself that learning is a fundamental part of this profession. Rather than worrying because you still have stuff to learn, you should instead be concerned if you don’t.
But even when we know on some level that this is true, it’s not always what we tell each other. And as a result, we do untold damage by making each other feel stupid.
What if we didn’t do this? Imagine a world where you arrive at your desk each day excited about learning new things. Where you never worry that you don’t know enough. Where there is no such thing as imposter syndrome. Because it turns out that being an effective IT professional is not about what you know. But we think it is — and that causes problems.
Problematic behaviours
I left the industry because I felt inadequate and stupid. It seemed that everyone else knew more than me. But I do a lot of teaching these days, which means I’m now very aware of how common this feeling is, and I’ve noticed some behaviours that I believe make it worse for all of us. I run through all of them in my talk, and I’ll present the following key ones in this article:
Laughing at others for gaps in their knowledge
Talking in jargon
Hiding a lack of knowledge
Gatekeeping
Laughing at others for gaps in their knowledge
I’ve lost count of the number of times I’ve witnessed colleagues emerge from interviewing a prospective employee and saying things like “Can you believe, I just interviewed someone who didn’t even know what a Z was?” followed by incredulous laughter. Haha, what a doofus, fancy thinking they could get a job here if they don’t even know that.
Don’t make people want to hide away!
For Z, you can substitute your favourite surely everyone knows about this item, and I guarantee I can find you somebody amazing, experienced and successful who knows nothing about it.
But it helps us bond with each other, right? If we can have a bit of a laugh about how stupid those other people are? And it doesn’t do any harm, because they’re not in the room. They don’t know we’re laughing at them. No, they don’t. But you do. I do. Everyone else in the room does. I’m certainly not the only person thinking, I must make sure I don’t become the next butt of the jokes. Instantly feeling stupid, as I consider all the things I don’t know enough about.
The worst thing about this is, I’m likely to handle this problem by taking steps to hide my ignorance. I’ll join in the next round of laughter extra loud, to prove I’m one of you clever people and not one of those stupid ones. I won’t ask questions to clarify crucial details. And I will find myself increasingly…
Talking in jargon
This is another one that can help us to bond with each other and feel clever, but it comes at the cost of making others feel stupid. Every time you make assumptions about what jargon others will understand, or even worse, judge them for not knowing the same jargon as you, you make it less likely they’ll ask for clarification. It becomes more likely that gaps in communication will widen, and your product will not behave the way people expect or need it to behave.
And of course, not only do people avoid asking for clarification, they take active steps towards…
Hiding a lack of knowledge
Of course we all do this. We avoid “asking stupid questions.” We exaggerate what we know in job interviews and on CVs. We don’t admit it when we find ourselves in meetings where we have little or no idea what anyone is talking about. It’s just self preservation, right?
The more we work in environments where knowledge is considered to be a good indicator of our colleagues’ proficiency, the more likely we’re not being honest with each other about what we know. The last time I was in the market for a new job, I reached a point where if I saw companies imply that they possessed advanced techniques for filtering out only the very best and rejecting the rest, I automatically rejected them. Because in those kinds of environments, people will go to advanced lengths to prove they are indeed the best… which means never admitting any weakness and therefore covering up the unavoidable fact that they don’t know everything.
Are these people happy and confident? No. They are likely peddling furiously underwater and permanently paranoid that someone will find them out… for what? For being stupid? Well yes, that’s what it feels like. But what they’re really in danger of revealing is their own basic humanity.
And of course, the kind of culture I’ve described above, the kind I came to deliberately avoid when looking for good companies to work for, is that of…
Gatekeeping
This is where we decide that we are special, and large swathes of the rest of the profession are not, and all we need is to perfect our formulas for identifying the pearls and rejecting the swine. And what does it do? Yep. It makes us all feel stupid, terrified as we are that we will come up against these standards and fall short.
Solutions
So, what can we do to address these problematic behaviours?
The Lucky 10,000
https://xkcd.com/1053/
As described in xkcd’s excellent cartoon above, today’s lucky 10,000 will learn something exciting and new. And the rest of us have a choice: Embrace that journey and travel with them, or stand on the sidelines laughing at them for not having learnt it yet.
Making that decision to get excited about the learning our colleagues have yet to do, is about a lot more than individual choices in single moments. It’s about recognising that the range of knowledge in our industry is very wide. It’s wide, and it’s widening exponentially all the time. You could take two respected professionals with long successful careers, and find no overlap in their knowledge. Hell, you could find a busload of them with no overlap. It’s pointless to wring our hands over what people don’t know, and much more enlightening to embrace the learning of our colleagues and ourselves.
They don’t know as much as you think they do
The wheel of confusion
I use this series of graphics a lot in my talks. Here I show only four of the images, but it’s enough to illustrate the point: When you look at an internal message board where your colleagues are discussing a range of subjects, it’s tempting to think to yourself, “Oh my, all these people are so knowledgeable and confident on all of these subjects, and I don’t know about any of them.”
But of course each one of those people is only posting confidently about some of those topics, maybe only one each, and chances are you have posted knowledgeably yourself on at least one of them. And they are likely suffering the same worries and insecurities as you are, looking at each other — and you — and thinking how much less knowledgeable they are than everyone else.
You’re not doing anyone any favours — least of all yourself — when you assume people know more than they do. We can all benefit from being realistic about each other’s capabilities.
Be confident in your own ignorance
At the start of this piece, I talked about how I ran away from this industry in relief, glad to be laid off and determined never to return. But a few years later I found myself in need of a new job, and recognising that despite having not previously valued them, those twelve years of engineering experience were worth something to me.
I returned to this industry, but this time with a different approach. In the intervening years I’d been working for a lower salary as a high school maths teacher, so it wasn’t hard to return to technology at entry level, joining all the new computer science graduates and behaving as though as I was brand new. This gave me a new lease of life. I wasn’t pretending to know anything I didn’t, but I was excited and eager to learn. I let go of any shame and made a point of asking simple questions.
Of course those previous twelve years did count for something, and it wasn’t long before I found myself in senior leadership positions. And it was something of a revelation to me that the more open and confident I was about what I didn’t know, the more people respected me.
Not only did it help me to progress and learn quickly, it also helped those around me to do the same. When senior figures ask simple questions and show no shame about what they don’t know, everyone around them is empowered to do the same. Everyone feels less stupid, and their knowledge and competence increase more quickly as a result.
A recursive function is a function which calls itself.
It sounds like a simple concept, but in practice it can be hard to get your head around.
Here’s a lovely example of a recursive function:
void ExplainRecursion () {
var explained = GetInput(“Do you understand recursion yet?”);
if (!explained) {
ExplainRecursion();
}
}
It’s particularly nice because it uses recursion to explain recursion. It calls itself – it’s a recursive function. Every time it calls itself, it asks the question “Has recursion been explained yet?” and if the answer is No it calls itself again. It keeps calling itself repeatedly until recursion is finally understood, at which point it quits. What might not be immediately obvious is that when it quits, it will still be inside the previous call, and will keep returning until it reaches the first time it was called. Like this:
ExplainRecursion()
—–>Do you understand recursion yet?
—–>No => ExplainRecursion()
———->Do you understand recursion yet?
———->No => ExplainRecursion()
—————>Do you understand recursion yet?
—————>No => ExplainRecursion()
——————–>Do you understand recursion yet?
——————–>Yes! => Exit function
—————>Exit function
———->Exit function
—–>Exit function
Exit function
It’s true that this is a slightly jokey example. It works if you see the source code, but if you only ever saw the output you wouldn’t learn much about recursion. You’d just be asked the same question repeatedly and probably get a bit angry.
Here’s another simple example:
void Factorial(int n) {
if (n == 1) {
return 1;
} else {
return n * Factorial(n-1);
}
}
This function is calculating “n factorial”, which is written as “n!” in mathematical notation. In case you’ve forgotten or haven’t come across this concept before, it’s easiest to explain with a concrete example: 5! (“five factorial”) is a quick way of writing 5 x 4 x 3 x 2 x 1. So n! means take the number n and multiply it by all the whole positive numbers that lie between itself and zero.
The above function calls itself, which means that unless n is 1 (because 1 factorial just equals 1), it will return a result… to itself.
Don’t worry if you’re feeling lost already. It took me a long time to be able to look at recursive functions without getting an instant headache. I found it helpful to draw little diagrams. For instance, if we use the above function to calculate 5 factorial.
Remember, the answer we expect is 5 x 4 x 3 x 2 x 1.
The first time we enter the function, we call Factorial(5), so n = 5. The function returns 5 x Factorial(4), so it gets called again and this time n = 4. That returns 4 x Factorial(3), which returns 3 x Factorial(2), which returns 2 x Factorial(1). Factorial(1) just returns 1. Here’s a diagram to help:
Factorial(5) = 5 x Factorial(4)
—–>Factorial(4) = 4 x Factorial(3)
———->Factorial(3) = 3 x Factorial(2)
—————>Factorial(2) = 2 x Factorial(1)
——————–>Factorial(1) = 1
—————>= 2 x 1
———->= 3 x (2 x 1)
—–>= 4 x (3 x (2 x 1))
= 5 x (4 x (3 x (2 x 1)))
Normally when a function returns a value, it’s over. We can move on. But with a recursive function, we might just land back inside ourselves again, and then we might return a value to yet another version of ourselves. With complex functions, this can be hard to trace through in your head without losing track. It’s also horribly easy to get stuck in an infinite loop.
Hopefully this helped. If not, don’t worry. It’s not just you! It’s a notoriously head-twisty concept.
“objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.” See also design by contract.
Code that uses a base class must be able to substitute a subclass without knowing it
The specific functionality of the subclass may be different but must conform to the expected behaviour of the base class.
EXAMPLE:
So, I can call FemaleMammal.GiveBirth and I can substitute a cow into that – Cow.GiveBirth without breaking anything
No matter which version I choose, I should be confident that I will end up with a baby animal. Even though the specifics might be slightly different.
These are the principles which must be adhered to:
Contravariance with parameters
There should be contravariance between parameters of the base class’s methods and the matching parameters in subclasses. This means that the parameters in subclasses must either be the same types as those in the base class or must be less restrictive.
Parameters in subclasses are either the same or have more / add extra functionality
There must be covariance between method return values in the base class and its subclasses. This specifies that the subclass’ return types must be the same as, or more restrictive than, the base class’ return types.
Return values in subclasses are either the same or have less functionality
The preconditions of a base class must not be strengthened by a subclass
A precondition of a class is a rule that must be in place before an action can be taken.
EXAMPLE:
FemaleMammal must not be a virgin and must never have had a Caesarian before giving birth
Cow must not be a virgin before giving birth
Postconditions
Postconditions cannot be weakened in subclasses.
Postconditions describe the state of objects after a process is completed.
EXAMPLE:
After female mammal gives birth, it must have an associated baby that is a mammal
After cow gives birth, it must have an associated baby that is a bovine (this is fine)
After cow gives birth, it must have an associated baby that is any kind of animal (this would not be fine)
Invariants
The invariants of a base class must not be changed by a subclass.
An invariant describes a condition of a process that is true before the process begins and remains true afterwards.
EXAMPLE:
FemaleMammal is female both before and after birth
Cow is female both before and after birth
History
New or modified members should not modify the state of an object in a manner that would not be permitted by the base class
EXAMPLE:
FemaleMammal gains a newly populated Baby property and the baby has a head
Cow gains a newly populated Baby property (this is fine)
Cow gains a newly populated CowUdders property (this is not fine)
Exceptions
A subclass should not throw exceptions that are not thrown by the base class unless they are subtypes of exceptions that may be thrown by the base class
EXAMPLE:
FemaleMammal.GiveBirth throws a BreachedBaby exception
Cow.GiveBirth throws a BreachedBabyCow exception (this is fine)
Cow.GiveBirth throws a NotEnoughGrass exception (this is not fine)
For instance, if you want a read-only file type, it should not inherit from File – and File should not have a Save method. Rather, the base File type has a Load method, and it has a subclass – WriteableFile – which has a Save method. Thus the base class cannot be saved. (otherwise you would have ReadOnlyFile which was not able to implement the Save method on its parent, and also left the object in a different state after calling Save than what you would expect – particularly if its Save method threw an exception which was not thrown by the base class)
While I was at NDC London last week I had a lot of fun chatting to @richcampbell and @carlfranklin for the .Net Rocks podcast.
We talked about a lot of things related to teaching and learning: different learning models, standards for education in software and how to create a culture that allows people to not know things.
There are some words and phrases whose precise meanings just won’t stick in my head. “Yak shaving” is one of them.
So I want to thank the author of this blog post, Yevgeniy Brikman, first of all for writing a great post about infrastructure, but also for providing a great visual aid to remind me what yak shaving is.
And I’m putting it here so that I can look it up, the next time I forget.
(Apologies, I would have embedded the giphy but I’m still using the web version of WordPress, which doesn’t seem to allow it. I will hopefully sort this out soonish).
I got pinged by my colleague @j_f_green this morning with the following message: “Hey Clare. Hope all is well. Was wondering if I could be cheeky and get your input on a (small) thing I’m trying to solve? I am searching for an elegant solution and I’m starting to suspect the answer may involve maths.”
…and of course I was instantly interested.
The problem
It turned out to be a relatively simple problem, depending on your point of view. If I say “GCSE-level simultaneous equations” your reaction will tell you whether that’s simple for you or not (for people not in theUK, GCSEs are the exams our pupils take when they are 16 years old).
Here is the problem: We want to discover two whole numbers (aka positive integers) that will give us the height and width of a grid in cells, given the following constraints:
We have a number, let’s call it capacity, which represents the minimum number of cells to fill the grid. We want at least that many cells. We’re happy if we have more, but not if we have fewer.
We also have another number, we’ll call it aspectRatio, which is the aspect ratio of the grid. This is the relationship between height and width. So for instance, if the aspect ratio is 2:1, that means the width is twice the height (it’s twice as wide as it is high). To reduce that to a single number, aspectRatio is 2.
width = 2 * height = aspectRatio * height
(aspectRatio will often be a decimal or a fraction, so for instance if width is half height, aspectRatio is 0.5, or if the ratio is 2:3 then aspectRatio = 2/3 = 0.66, and width = 0.66 * height)
A concrete example
Here is a concrete example: Let’s say we have a grid that is twice as wide as it is high, so aspectRatio = 2. Let’s also say that our capacity is 200: We want to fit at least 200 cells into this space. I’ve deliberately chosen an easy example, and the answer in this case would be a width of 20 and a height of 10:
Apologies if that’s making your eyes hurt. Is it making your eyes hurt? It’s making my eyes hurt. I’ll try and make a better diagram later. It’s not even got the correct aspect ratio on the page…
If you enjoy attacking maths problems, look away now. Have a go at solving it before you read on. You may come up with a better solution anyway.
So how do we compute the answer?
One way would be a loop. Here is some code that would do the job:
var capacity = 1000;
var aspectRatio = 1000 / 800 = 1.25;
Here we have a grid that has at least 1000 cells:
36 * 29 = 1044
It has roughly the correct aspect ratio:
36 / 29 = 1.24
1000 / 800 = 1.25
The problem with this is that it does not scale well. The higher the numbers, the longer the loop. But fear not – maths to the rescue.
A simple use case
Let’s start with a simple use case: imagine the aspect ratio is 2:1, and the width is twice the height. Also let’s say we’re trying to fit 200 pixels into the space.
Let’s call width and height x and y. So:
x * y = 200
and
x = 2 * y = 2y
If x = 2y then x * y is the same as 2y * y, which is 2y²:
2y² = 200
y² = 100
y = 10
x = 20 (because it’s 2y)
A generalised solution
The first block above gives us our two simultaneous equations.
We can generalise the same approach for all numbers. Let’s call the capacity a, and the ratio b. Using our equations from above :
x * y = a
and
x = b * y
These are our simultaneous equations. Using the same logic as above, we substitute y for x and end up with:
by² = a
Which means that
y² = a/b
and
y = sqrt(a/b)
(where “sqrt” is square root).
Then once you’ve calculated y, you can calculate x:
x = b * y
The only missing piece of the puzzle is that we are dealing with inequality, not equality. So really it’s this:
y >= sqrt(a/b)
This just means that once you get a result, you round it up to the nearest integer. Same applies to the final substitution : if b * y is not a whole number, then round it up.
When you calculate b * y, you should use y BEFORE rounding.
So to finish, let’s stop being mathsy and start being softwarey. Let’s give our variables some meaningful names:
height >= sqrt(capacity / aspectRatio)
width = aspectRatio * height
If we test it with the example we used in the loop above, where aspectRatio = 1.25 and capacity = 1000, we get the same results:
Compared to the loop we started with, this scales brilliantly. It doesn’t matter how big the numbers are, the compute time is basically the same. It has a constant computational complexity, otherwise known as O(1)*. The solution above using a loop gets slower and slower depending on how big the numbers are. It has a computational complexity of O(n), so it’s not as good.
Hopefully that makes sense and I didn’t over-simplify. All feedback welcome.
* I couldn’t remember how to express O(1) so I initially guessed at O(k), because I was thinking of k as a constant. Sadly I was wrong, because I rather like the thumbs-up “Ok” concept.
These may well be the notiest notes I’ve ever published, but just in case they’re of any use to anyone… if nothing else they may whet your appetite for the new edition of Martin Fowler’s Refactoring book.
I confess I never read it first time round (not on purpose, just there are so many books in the world and so little time…), so I’m looking forward to reading it this time. It hasn’t come out yet in the UK but should be some time in the next few weeks. Mine is already on order [drums fingers impatiently].
So anyway, Martin did a webinar today on the topic of his new book, and here are my notes:
Refactoring should be done via small changes
v small semantics-preserving changes
So small they’re not worth doing on their own
String together small changes to make a big change
When adding new functionality:
Alternate between refactoring and adding functionality
Often it’s easier to add new functionality if you refactor first
Balance between adding new functionality and refactoring – it’s a matter of judgement
V1 of Martin’s Refactoring book vs v2
Some things that were included in the first book he decided not to include I the second
Eg Unidirectional vs bidirectional – not important
Some things he thought were too trivial for the first book, he has now changed his mind about and included
Eg moving statements within a function
Most notable changes = no longer centrally object-oriented
“Extract function” used throughout instead of extract method
One of the refactorings is around the choice between OO and non-OO
The most visible thing here is the choice of language (javascript)
But language is not necessarily relevant anyway
OO vs functional
He doesn’t see that as a huge shift
OO should still have functional elements – referentially transparent
They are overlapping paradigms, not distinct
Favourite refactorings?
“Split phase”
Hunk of computation which can sensibly be divided into two phases with a data structure communicating between them
Eg parsing – separate the tokenising out – deal with a series of tokens instead of a stream of text
But if you split a loop, what if you are introducing performance problems? See below…
Performance and refactoring
Split loop is something people often worry about because it will run the loop through twice
Most of the time it doesn’t matter – a push for clarity will not change the performance of the code
Most of the time by refactoring you open up an opportunity for performance improvements you would never have noticed otherwise
Then again you should run performance tests frequently
Not necessarily with every build, but every day or two
But most micro-changes have no impact on performance
You will only find real performance impact by performance testing
Always have to ask, how do you make the smallest possible change?
Eg extracting a data-intensive microservice from a larger system
Most of the moves are happening within the monolith BEFORE you start firing up the external system
Then you move stuff in really small pieces
Things that change for similar reasons should be together – you need to see the relationship between them
Branching / trunk-based development
Develop on a branch and merge at the end, with branch being long-lived – days, weeks or months
Still a really common pattern
But if something has been refactored, you don’t find out until you merge – this can blow up on you
It means you become scared to refactor! There’s a disincentive.
Continuous integration: Integrate with each other fully at regular intervals – at least once a day
Every day you receive everyone else’s code changes and push your own changes back
Removes the big painful merge problem – not saving up for a big horrible crunch
Adage of Apollo – it hurts if you do it more often (?)
Sorry, I wrote this down but I’ve since Googled it and I don’t really know what it’s about – I may have misheard!
Open source development:
Actually this CAN be a good place to use branches, because everyone working separately and you can’t guarantee all changes should be merged
People call it trunk-based development but “continuous integration” is a better name because that’s what it’s really about
People worry about continuous integration but it’s less painful than they think
If you do integration every day it becomes easier and less painful until you stop even noticing
Responsibility
Keep code base in healthy state
If not you are stealing from colleagues, clients and employers!
But no point saying you should do it for moral reasons, that won’t motivate people to do it
It’s economic: Want to get more changes out more quickly
It’s professional: We’re being paid to produce features rapidly
It’s not up to managers or business: They don’t know how to keep code healthy – that’s what they pay us to do
Refactoring and design patterns – are they incompatible?
With a lot of patterns, you shouldn’t introduce them right away cos they’re not appropriate and you don’t require the full strength of them yet, particularly the more complicated ones
Introduce patterns as a result of refactoring: Refactor towards them
There were some gotchas though, so (as always) I made notes:
Whenever you add new code you have to stop running and rebuild before you can send successful requests.
When you do a POST, the port number is 5001 or 5000: It’s whatever you’re using in the browser for GET requests
When you add the Update method, you have to send another POST to create a new Todo item before you can update it. The url is the location field from the response of the POST.
When you add the Delete method, use the GET request in the browser to find the Ids of existing items so you know what you can delete. The url is the same as the Update url, with appropriate Id added on. Use the GET request to check whether deletes have worked. Use the POST to add elements you can then delete.
When you add a new database context you need to update the dependency injection stuff in Startup.cs and you need to make sure your POCO class has an Id property.