Recombine to avoid naming when naming is hard
lispcast.com
lispcast.com
"At all cost" even at the cost of readability?
It also ignores the possibility that good names do exist, and that they contribute a lot to maintainability.
The suggestion made is when a good name "cannot" be found, you should try to avoid naming. No. Bad suggestion.
When a good name cannot be found, you probably have muddied up the problem, and should rethink it. Or try harder to find a good name. Also re-examine your ideas about what is "good", avoiding false metrics such as "names need to be under three characters long".
Absolutely. Software as we know it could not exist without abstration, and abstraction can not exist without names for things. Sometimes a new name is required because a particular concept or application is relatively novel, but if you decline the challenge of naming it then progress stops immediately. No future applications will be built on top of an unnamed abstraction.
I think the software development industry should stop doing such a disservice to itself, and start funding more scientific studies in software development.
Random, almost shower-thought hypotheses like 'naming things is hard - we should stop doing it' are given way too much credibility with nothing to back them up.
> the software development industry should stop doing such a disservice to
> itself, and start funding more scientific studies in software
> development.
Yes. > hypotheses like 'naming things is hard - we should stop doing it' are given
> way too much credibility with nothing to back them up.
Also yes, but let's separate generating hypotheses and discussing them from giving them credibility. It's a net win for people to share their shower-time hypotheses with each other. It's a net win for sites like Hacker News to feature them so that the "long tail of thought" can stretch ever longer.But as you say, we need to keep some perspective and grant that these are hypotheses. For example, a lot of times, people try turning a dial to eleven:
Long iterations are hard, what's the shortest iteration cycle? What if we try releasing every two weeks?
Code review is valuable, what if we review 24/7/365 by pairing or even mob programming?
Long methods are hard to understand, what if we make a rule that they can be no longer than five lines?
And so forth. And they get good n=1 anecdotal results. My hypothesis is that there's a Hawthorne Effect going on: When you turn a dial to eleven, you find yourself forced to observe and think about what's going on very thoroughly, and in the process of doing so, you get better results.
Then you keep doing that, and eventually the new way of doing things is internalized, and you stop being forced to think very hard about things, and you regress to your old level of hand-wavey productivity/quality/whatever.
This little pet model of mine explains why things like this keep popping up, why they are often valuable exercises, but also why, as you say, we should do some real measuring before pronouncing that we're DoingItWrong™.
There's an entire field called Software Engineering. But, it's more fun to just do what feels quick and satisfying instead of binding yourself to processes other people dictated.
Unfortunately, I feel like that term has been very diluted in the last few years, to the point where I'm not sure it has much difference (at least colloquially) from "programming".
https://en.wikipedia.org/wiki/Software_engineering
Then you have Agile and XP and Scrum as either fast-flux Software Engineering (without all the "formal process/documentation" and instead use informal, but blessed, processes and immediate, undocumented, decision making) or anti-software-engineering depending on how you look at it.
Think: space shuttle or airliners, not social media. Sure, they still aren't perfect, but if you told me my airplane ran on Ruby I'd rather walk to my destination.
The initial page for it on wikiepdia is interesting too (November 14, 2001): https://en.wikipedia.org/w/index.php?title=Software_engineer...
Especially: Methods have improved through the 1980s and 1990s, but the complexity in requirements have increased fast enough to consume any improvement.
There is also a constant eagerness to develop new tools and try the newest tools in each new project.
If the programmers are good, the system will work, and the customer will not fully appreciate how complex the task was.
Agile is about building the right thing. We found out that gathering the requirements up front and developing an extended timeline and holding deviations to change control ended up building the wrong thing because adapting to change was too painful.
We learned that if we wrote software that documented its behaviors in code, it was sometimes more useful than snapshots of technical documentation. Sometimes that isn't true (public APIs and protocols are the first two that come to mind), and thus the appropriate documentation at the appropriate level is justified.
We learned that even the best engineers are very poor at how long a task will take, but they are relatively good at estimating how long a task will take relative to other tasks. We learned that we could measure how long these tasks take and get a better estimate of the length of time for a total project.
The problem is that the SDLC is a bad process for many classes of software that aren't in the embedded space. Agile is good for some problems (IE, where the requirements are poorly understood) and not others.
That's the true answer. There's no one-size-fits-all solution, but people think (and argue) in absolute terms, not situational nuance.
People routinely mistake knowing the end-goal requirement ("Make a healthcare website") with knowing every sub-task requirement ("uh... we just spent $500 million with the goal of making a website and nothing actually works!").
We're in a fast society. Nobody wants to spend three years in pseudocode and dependency evaluation and documentation and procedures before laying down real code, so the best we can do these days is augment our decision making as our understanding of the problems and goals evolve then resolve all conflicts and confusions in as real-time as possible.
Hm. I wonder if some of the larger companies are doing that sort of thing and keeping it internal-only or using it for internal coding standards without publishing it in an attempt to gain competitive advantage?
There's some evidence to suggest that the reason why studies like this so often produce seemingly conflicting results is that there aren't very many universal rules. The same practice can be helpful for one team and harmful for another based on any number of circumstances.
Overall, software development is looking more and more like a journey to me. And a journey in the polar sea is different from the journey in the sahara. Just don't leave jenkins at home in either case.
Computer Science mixed with human factors looks similar to psychology in the early 20th century (we clearly have mommy problems). The statement "there are only 2 hard problems" is cute and pithy but doesn't really match my experience over the last 15 years.
Most of my painful memories relate to: multi-threading, timezones (bloody timezones), migration a system from A to B tech (i.e. SQL to NoSQL), ensuring a distributed system does something once--and only once, refactoring a god object, delegating a problem to an uneven strength team, building a system where the team organization doesn't match the desired architecture, getting the right requirements, dependency management, and deployment. There are probably more but that's beside the point.
There is research to support some of my observations but it's hidden behind paywalls or has thin supporting evidence. Comprehensive studies are hard and a lot of the research uses only students as convenience subjects. There really isn't a strong connection between the research community and practice in order to provide the right measurements.
What I would like to see is adding support to GitHub for community research studies. There is wealth of pure code observations. It would be interesting to allow integration of survey tools and marry that to the code observations. It'll answer some of the problems but not others (i.e. the pain of maintaining a live service). And finally as part of the GitHub EULA this research must be publicly available.
For example, functional programming is terrible for beginners and has a high learning curve. People who use it in production swear by it. How do we test this?
Can we trust that what is true for a beginner is true for an expert? How do you afford 10 mid-level programmers for 6 months? Is that a large enough sample size?
The fundamental problem with studies, as I see it, is that we haven't figured out how to constrain the variability yet. We have a lot of work to get out of the hypothesis stage still.
If you have hard time naming something in your code, chances are it's because you have a nebulous understanding of that elusive thing. This is obviously bug prone. It's usually a good idea to spend a few minutes searching for a good, precise, descriptive name, and add enough comments if the entity being named is not a temporary variable.
Doing so not just makes later code maintenance easier, it tidies up your understanding and can even lead to finding a bug or another problem (happened to me more than once).
In the most extreme case, we've taken like half a day off with the entire team to discuss just about everything. We went from a name to one of our software to our architecture to our domain and it was just half a day to a day of discussion and merging understandings.
After this, we spend another half a day to a day just cleaning up a huge mess of potential bugs, and after that, naming was simple. And now, a few month later, hell it was worth it.
This makes me think of prescription drugs, and how they all have 2 names, one for marketing to the public, and one for marketing to doctors. [e.g. "Ask your doctor if Savator is right for you. (Dipromelexeline)"]
I agree, that naming is hard, of course.
But I don't agree, that you should avoid it at all costs!
Sometimes, the costs are just to high. When by avoiding naming things, you make the program more difficult to understand, you loose much. This could cost you nearly as much, as a bad name and very likely much more than a half-good name.
We changed the title to a representative sentence from the article.
So, sure, you can write:
function processData(err, data) {
//blah blah does stuff
}
db.getData(processData);
But it's actually a lot simpler and more understandable if you just write: db.getData(function (err, data) {
//blah blah does stuff
});
If and when you ever need to break that function out into something re-usable, give it a name then, but don't bother with a name unless you need it. function CalculateSalesBonus(err, salesmenEmployedByTheLastOfMonth) {
// do stuff
}
which actually contains much more information than db.getSalesmenEmployedByTheLastOfMonth(function (err, data) {
//blah blah does stuff
}); > Use threading. Instead of naming each intermediate value, thread the value through the process without naming it.
What does that even mean?To be honest, I'm not even really sure what the author's point is. Naming is hard? Naming is bad? Don't name things? Name everything? It just feels like an abstract nonsense piece.
It means don't do this:
int x = k + 3;
int y = v * x;
int z = y - foo();
return z;
Instead, do this: return (k + 3) * v - foo();Also, for the record, I totally disagree with the author on this. This:
return (k + 3) * v - foo();
works great, until you need to debug it. Good luck then.I do think it's a smell if the expressions has side effects, but that's a different issue.
In the toy function given, it doesn't really matter, but in general, I think that people should aim to code for clarity, not ease of debugging.
(defn debug-it
[x]
(println x)
x)
(-> input-value
debug-it
function1
debug-it
function2
debug-it
function3
debug-it)
Debug-it could also take a custom message at each line if you wanted the output tagged with names/numbers. A = max(3, 5)
B = max(A, 7)
C = max(20, B)
C = max(20, max(max(3, 5), 7))Here is an example of the main part of a Haskell program (using the "pipes" combinators). It starts two concurrent pipelines. One reads user commands from a queue, encodes them, then writes them to an AMQP server. The other reads results from the AMQP server and runs them through a more complicated pipeline, the details of which aren't very interesting.
runEffectsConcurrently
(contents commandQueue >-> encoder >-> writeTo amqp)
(readFrom amqp >-> decoder
>-> skipEmpty
>-> logToFile
>-> forever (decodeMsg state)
>-> toChannel eventQueue)
What I like about this style has to do with how it avoids naming intermediate results. Instead of stuff like loops, anonymous callback functions, and variables, it approaches a declarative specification of the program's structure & behavior. I still had to give names to each component, but I didn't need to come up with names for the values that flow through the pipelines.Beyond that, though - the above code looks great - until you need to debug it.
Personally, I'd rather pay a 'naming tax' on things for the sake of readability / debugability over what the original author is shooting for any day.
Such pipelines aren't necessarily made up of I/O actions though; they can also be pure computations, which cannot perform side-effects such as logging. In that case, you can still insert debugging helpers, but you might need to be slightly creative. For example, if your pipeline generates a list of values, you could put debugging entries into that list.
Generally, though, the saving grace in such a case is that each component will be a pure transformation, which means that you can easily call it with a test input and inspect the output.
There are some step-by-step debugging tools for Haskell, but I haven't used them, and Haskell programmers mostly seem uninterested in that way of debugging. It doesn't fit very well with Haskell's evaluation model anyway. When I work with Haskell, I just debug in a different way, focusing on verifying correctness of functions and their combinations.
I created hs-scrape[0] to write web scrapers and I frequently use the ghci debugger[1] for step-by-step debugging.
0: https://github.com/codygman/hs-scrape/
1: https://downloads.haskell.org/~ghc/latest/docs/html/users_gu...
I have to say, minimizing intermediate holding names makes debugging and writing clear code much easier. 97% of the time, I never miss the "naming tax".
(~> (sha1 in)
(bytes->hex-string)
(bytes-append #".")
(displayln))
In pseudo-Python this would be: displayln(bytes-append(bytes->hex-string(sha1(in)), #"."))
http://www.greghendershott.com/2013/05/the-threading-macro.h...Note well: This is not "always". Do not do this with every single line of your program. This is not even "often". But once in a while, it can dramatically improve clarity.
"If you are having trouble naming something, then you don't fully understand that something."
What I get from that saying is that if I have a function that I am struggling to name, it means that I don't fully understand the problem, and there's a good chance that it is doing to many things. Keep your functions to performing a single task, so they have just that one purpose, then name it after what it does. This improves readability and makes naming things much easier.
You have a variable you can't name, well, what data is it actually holding? If it is holding more than one piece of data (reused) then you are doing something wrong or very hacky.
Follow the same rule as with functions. Keep your variables to holding just one specific piece of data, and name it after that.
Both of these rules not only make naming things much easier, I've found they produce much more readable code, easier to debug code, and generally much less bugs in the code.
Declarativeness of the system was really nice and named rules really made sense, if the actions you can take are really meaningful. It was more like naming every function set while doing FP.
On the opposite end, I will sometimes go out of my way to name something when there isn't an immediately apparent need.
Also naming can often shorten up a long line.
Trivial example:
In [15]: SortByLen = lambda x: len(x)
In [16]: SortByLast = lambda x: x[-1]
In [17]: listOfInterest = ['123', '234', '432''12', '12345', '123']
In [18]: sorted(listOfInterest, key=SortByLen)
Out[18]: ['123', '234', '123', '43212', '12345']
In [19]: sorted(listOfInterest, key=SortByLast)
Out[19]: ['43212', '123', '123', '234', '12345']There are basically two reasons to name things.
1. Because something is lacking in either the syntax of our PL or our skill in coding, so that we end up giving something a name, when it does not really need one.
2. As a handle on some abstraction, indicating what it is for.
When #1 is the issue, names are best avoided. When #2 is the issue, names are a Good Thing, but a poorly chosen name can be very harmful.
Read this paper (for example) and tell me that naming and cache invalidation are harder than that: http://www.sigops.org/sosp/sosp09/papers/klein-sosp09.pdf
There are only 2 hard problems in programming:
1. naming things.
2. cache invalidation.
3. off by one errors.
They use models or paradigms with lot of names mainly because of lack of theory than can connect human sciences to mathematics or physics. In each model they can solve some aspects but the whole.
It looks like zoology or biology before DNA.