I just came up with a list of things all programme...
# thinking-together
k
I just came up with a list of things all programmers can agree are important, that non-programmers or newbie programmers tend not to focus on: * catching problems early * controlling evolution over time ("what did I change recently?", "need to move slow now") * controlling the space of inputs ("does the program still work in this situation?", "this feature adds too much complexity") * controlling dependencies I'm curious to see other people's lists, and whether anyone disagrees with one of my bullets. (Inspired by one part of https://www.capitalone.com/tech/software-engineering/go-is-boring)
💯 1
➕ 1
o
Looking back on when I used to code for fun vs. Professionally the biggest one is definitely evolution over time. I certainly wasn’t thinking about if a thing was easy to read, modify or understand. The task was just make it work. I think that’s the central difference.
➕ 1
👍 1
j
I really like these. I might add: * Understand the bug before you fix it.
➕ 1
❤️ 4
o
In the same idea than @opeispo, one that comes to my mind: good naming is important but hard.
w
I'd say a newbie by definition does not know which way is up. Cannot tell good practice from bad. At least that was my path. 😉
w
• controlling invariants
👍 1
• maintaining performance
s
A possible issue with such a list is that different people can interpret these items in opposing ways. For example, “catching problems early” to one person might mean catching typing bugs, to which they might propose a strict type system, while to another it might mean catching design flaws, which might lead them to favor a more flexible type (dynamic or optional) system.
👍 3
k
@Steve Dekorte Yeah, that's becoming clear to me as well. I intended "controlling evolution over time" to mean just a low-level sense of tracking the past rather than maintainability in the future. Not that maintainability is unimportant, of course. Your example is actually not that serious. I was deliberately trying to focus on the shared activities we perform as programmers rather than the diverse tools we may use to help us do them. Things that require manual consideration regardless of what tooling we may use to reduce the effort of acting on this consideration. But yeah, it's clearly a question in progress, and I'm failing to articulate boundaries crisply. Basically I'm imagining a series of yes/no questions of the form "do you think about _?" What would all programmers above some level say yes to that programmers below wouldn't? In what ways does the practice of programming rewire our brains? Quite possible my initial answers don't fit.
s
I’d guess I’d file these under “managing complexity”. The other under appreciated sub-areas I’d add are maintainability, portability, persistence, synchronization, communication, and security.
k
I don't have much experience dealing with parallel or distributed computing, and I have zero experience with security. I think portability is irrelevant to 90% of programmers today. So -- to the extent that I'm a programmer -- your list seems quite aspirational.
s
When was parallel/distributed computing mentioned?
d
This list feel more like language/tooling smells rather than coding requirements. What's stopping you from moving faster, adding more features, using some dependency, or handling problems later? These are super valuable questions for a language designer...
k
> .."need to move slow now"
What's stopping you from moving faster?
The scenario I was thinking of: I often want to do some big fuzzy thing, and I realize just how big it is after some false starts, and start to think harder about decomposing into smaller pieces. So what I initially thought was going to be a single commit eventually turns into a series of commits. And in the process my understanding of the problem becomes much more precise. "What tooling can get rid of the process of clarifying one's understanding?" feels as hard a problem as "world peace".
> .."this feature adds too much complexity"
What's stopping you from adding more features? ... feels more like a language/tooling smell
I'm thinking here about the principle of parsimony in design. There are two ways to add a feature. One yields a state space of size
M+N
, another of size
M*N
. The extra complexity in the latter isn't needed for its purpose. Again, no tool is going to help you with this sort of problem until tools become sentient and turn us out of jobs. More strongly, I make the case in my recent paper (http://akkartik.name/akkartik-convivial-20200607.pdf) that all features have costs, and end-user computing requires making end users aware of these costs, so they can better decide if they need them. "Zoom's going to start a webserver on your computer which adds a little convenience but a lot of risk. Yes/no?" Tools for handling problems later.. just, no. Say you're building a prototype, and you don't care about a bunch of things. There are still things you need to get right that will otherwise pollute the signal you're hoping to get. No tool is going to substitute for the imagination needed to anticipate how your A/B test might get messed up. With all the attendant slow-down and context-switching overhead that each such bug causes. It's pretty clear now that my original prompt was abysmally unclear. Hopefully this helps clarify things.
👍 1
@Steve Dekorte sorry I dropped your (rhetorical?) question. What I was getting at is the distinction between using synchronization primitives and having to reason about synchronization. Every time I create a shell pipeline I use synchronization. But I don't need to think about it. Do programmers need to think about or appreciate synchronization more than they do today? I'm not yet convinced. And similarly for many of your other axes. They're the stuff of CS degrees, sure, and I have a CS degree so I'm closer to them than most. But they feel like accidental complexity in most situations.
d
@Kartik Agaram thanks for clarifying! In this case I have a few more thoughts that may be useful: * Designing a language to purposely help increase the understanding of the problem would be interesting. Maybe allowing for both specific -> general (like TDD) and general -> specific (like Haskell type declarations before impl). Idris and type-driven development is one angle that's interesting here, and rust/elm/purescript+typedholes also leads devs to have a "conversation" with the compiler as well. (FWIW, I'm fairly ignorant of Mu so unsure what opinion it takes here, if any). * M*N situations do arise sadly. Most of abstract algebra and category theory is about describing ways to simplify and (hopefully) efficiently compose these cases. I believe that tools can help us with these, such as statebox, but the barrier to entry feels pretty high still. I worked on this a bit at Invision too (fun exceptions when nesting state machines that non-programming designers needed to be able to "configure"). [Side note: M*N problems are particularly annoying to represent in a linear view like text-based languages. There's fun stuff from the category theory people on diagrammung multi-dimensional composition "easier" but I haven't seen non-math nerds be able to develop intuition around them yet. ] * Delaying complexity until later is super important to prototyping quickly. The problem is knowing that you skipped over something and if you are "polluting your signal." I believe the sweet spot here is gradually typed languages, though I'd say they have mixed results in terms of lowering complexity so far. I'd bet that AI would be able to "fill in the blanks" sooner than they could design the skeleton.
s
@Kartik Agaram wrt synchronization, I was thinking about synchronization between UI, model, and database. This is an area that most developers would not think much about until they took a close look at how much of their code base (an bugs) is consumed with code that deals with these issues in poorly designed and inconsistent ways.
❤️ 1
k
Oh, I totally misunderstood what you meant by the word. Yes, the importance of keeping two distant sub-systems "in sync" in some way that's hard to express -- absolutely fits my original question.
s
A garbage collector could be seen as one form of automatic synchronization (of the free memory table and referenced objects). Either you do it manually (which involves tons of code and bugs) or you abstract it such that it can be done automatically (with far less net code and far greater reliability).
💡 1
k
@Don Abrams I still feel like we're talking past each other. Yes, tools can amplify capabilities at various tasks. But if we're trying to teach the elements of programming thinking, tools can paradoxically make that more difficult, by reducing the opportunities for some sort of thinking or other.
👍 1
To back up to your original comment:
This list feel more like language/tooling smells rather than coding requirements.
Are there any things to your mind that programmers have to think about? Or are they all 'smells'? Is the role of the language designer to eliminate the need for thought altogether? How do you draw the line?
d
Awesome question! To me, the definition of the problem and its constraints are unique for each program and cross all languages (by the writer, not necessarily the reader, and not necessarily explicit in the code). Once you get to the how, opinions start. The role languages play is forcing or allowing a programmer to answer certain questions (usually some variation of where and when).
k
I don't follow that at all.. 😄
@Don Abrams Still thinking about this thread. Sounds like you're a relativist where I was asking for absolute answers?
d
Maybe? I think programmer-required important things would be equivalent to "what would I need to know to write a program?" I believe these aare revealed a bit more by this question instead: "what would someone else need to know about this program to write it in another language?" In this case it's more about the problem and its constraints rather than dependencies, input space, etc. The role of the language is to make these things clear (to human readers/writers) while also running efficiently (clear to the machine what optimizations it can/should take).
p
@Kartik Agaram Hah, you are reading my mind! These are my top priorities as well! I’d add that, which is remained unsolved to me in the past years, and maybe this is the main driver behind all of my research: Be ready to change/delete any part of the code any time! Mixing it with your 2nd (controlling evo) and 4th (controlling deps) point: avoid broken abstractions - AKA - don’t write (or evolve to!) code which forces you to delete more (prod+test) code than neccesary (in case of new requirements) - AKA - eliminate God Objects (don’t even let them birth!) -AKA - Minimal surface: don’t make available any data in any given scope you don’t use => if you do, you are creating a God object and you have to REIMPLEMENT the stuff after deleting the nighmare and all of its connections. Sure it is an obvious idea to do that, but hard/impossible(!!!) to do without the correct level of abstractions (I think anything “below eventStreams”). To me it seems like we might need some level of redundancy to get there, but I’d pay that price if I can make sure the freaking God object and spaghetti bs goes away… This is just a deep intuition at this time, but I’ll update on that later.