Is Software Engineering Possible?
vanemden.wordpress.com
vanemden.wordpress.com
But if being able to program means being able to write large programs and get them perfectly correct, then no one has ever been able to program, the great Prof. Dr. Dijkstra not excepted. We all have puny brains next to a million lines of code. That being the case, the study of "how to program when you cannot" is critical for those of us who actually need to write these large programs.
I like to say that software engineering is the art of managing complexity. Designing abstractions to be as general as possible, with interfaces as simple and clean as possible, and minimizing coupling, makes it much easier to think about how the major components of the system interact. And that's the key: if it's too hard to think about the system at a high level of abstraction, because the interfaces have too many special cases in them, more mistakes will be made.
I don't think I can agree with the OP that formal verification is the only thing that can fairly be called software engineering. I do agree that the more verification we can figure out how to do, the better. But I would point out that well-designed abstractions are still important even when you're doing formal verification; indeed, possibly more so, because without them, the proofs get to be even more complicated and difficult than they need to be. It's really the same phenomenon: systems that are hard to think about are also hard to prove things about.
I think this is a false statement. In fact I think you're confusing modularity with abstraction.
Abstraction hides complexity, and abstractions are leaky; how the whole behaves can be strongly influenced by behaviours that leak from the abstractions. It took a couple of goes, but eventually we realized that DCOM and CORBA were poor ways to design distributed applications.
Abstractions should be as general as necessary, where necessity is subject to judgement.
Make code too abstract, and people may not understand it because it's not concrete enough, full of indirections and compositions and factory-factories; make it too concrete, and people may not understand it because it's too complex.
Creating an abstraction to share some code may pay off if there's sufficient clients for the code and they all have a similar usage profile, but it may be a false economy if the clients are few or the usage varied.
Modularity isn't an unalloyed good either. Too many modules and you may write code that acts optimally in a local way, but suboptimally globally because it has no access or awareness of other modules' behaviour. Modules designed around an abstraction may be substituted, but designing for module replacement is more often than not over-engineering. It's strongly encouraged by modern OO testing approaches, but IMO it puts the cart before the horse.
I'd like more people to realize that abstractions represent unknowns, and that any given implementation needs to be tested in an engineering materials sense, to find out what its force limits are, how it reacts under scale and pressure, so people using the abstraction are not just thinking about the abstraction, they are considering the real thing they're dealing with as well. It saves a lot of time going down dead ends.
The most vital abstraction is that most software is blithely unaware of the physical construct embodying it.
Abstraction isn't the enemy. I think much of what we call good "software engineering" is identifying, designing, and implementing the "right" abstraction.
Abstractions are often leaky, yes; sometimes unavoidably so, and then those leakages have to be taken into account in the design of the system; but when leakage is avoidable, do you not agree that it's important to avoid it?
I do agree that there's such a thing as over-abstraction, and over-engineering in general. I recall a discussion here on HN, oh, probably 2 or 3 years ago if not more, where somebody said they had written an app and then hired a programmer to maintain it, and the programmer started by "enterprisifying" it, adding lots of dependency injection and factory-factories and suchlike, rendering the code almost unrecognizable to the initial developer. Without having seen the code in both "before" and "after" states, I can't form a firm opinion about whether this was really an improvement, but I could easily believe that it was not. Certainly if I were the initial developer, I would want to know specifically what the benefit of each of these changes was expected to be.
What does it mean when someone is only able (or only wants) to write small programs?
For example, small programs that can work together to form a "system" for managing something.
Is that not considered "software engineering"? If not, what is it called?
(Embedded software engineering? But not all small software is "embedded".)
What has a better chance of being error-free, a small program or a large one?
Should "ability to program" be measured by how large a program one can write?
For example, if someone writes a small UNIX utility, but does not write a large program, should we assume he does not know how to program?
> Should "ability to program" be measured by how large a program one can write?
Yes, absolutely: that's a very important measure of programming ability. As I think Fred Brooks pointed out in The Mythical Man-Month, each order of magnitude in program scale requires new techniques and a new way of thinking.
I wouldn't say it's the only measure of programming ability; there is a place for finely crafted small modules. But on any project that's setting out to build a substantial piece of software, you need at least a certain number of people who know how to do that (then they can teach the rest). If nobody knows how to do it, the project is likely to fail.
I've been trying to find the right way to phrase this, as it's more of an inkling but touches on my current academic (self-study) interests but also is something I've observed over the past decade or so as a professional developer and tester in the industry.
The engineering component of software engineering needs to study systems engineering. There's a great deal of overlap. My conjecture is that we move from "programming" to "software engineering" when we are more interested in the connections between components rather than the components themselves. Much like how systems engineers aren't doing the work of designing the new car engine, but are overseeing that work and the work for the production line itself and coordinating with the glass manufacturer for the new windows. They care about how different parts of the final system (at the level of the car, or the level of the whole assembly process) will function between each other. Systems engineering (as a discipline) has the tools to handle this sort of division of labor and abstraction and responsibility.
And it's not really that we don't care about internal behaviors. But that's not the primary concern of the overall system engineer. It's the component's engineer who's primarily responsible for that, while the system engineer needs to focus on how that portion fits into the overall architecture.
Now - you could go down to machine code, or work on embedded systems, etc etc - but the point remains. It's not just the software you are writing that's part your program, it's also all the other, existing software (and physical designs) it makes use of, or interacts with.
Edit: Might be called "software systems engineering", or maybe "dev ops"?
If I'm conforming to someone else's interface (say the C standard library), that's equivalent to an electrical engineer assuming various standard interfaces for the equipment they're designing. It's a design consideration, but not a design task. So the project itself may still remain quite small.
When I'm designing the interfaces that others are using, or designing the interfaces between 100 [0] smaller programs so they can interact effectively with each other and their environment, then it's a larger engineering task.
[0] Arbitrary number.
A hidden agenda Joseph Goguena, Grant Malcolmb http://ac.els-cdn.com/S0304397599002753/1-s2.0-S030439759900...
Software development is more reliant on discrete math (and more generally the ability to reason abstractly) than math that helps model the physical world. Hopefully, Professional Engineering bodies will one day realize this and will adapt their program accreditation criteria accordingly.
The reason people think that is because they have no idea how the engineering to build a bridge or a car is done.
Note, the term is Software ENGINEERING not Software Assembly or Software Construction. The engineering work necessary to design a bridge or car is actually not TOO dissimilar to designing anything else. You have requirements (often vague, conflicting and changing) and constraints and you have to trade things off against each other to come up with a workable design solution.
The construction is just the compilation. Or perhaps the final coding/debugging. Or a combination. In construction, tiny engineering issues are often resolved by the construction crew at the site. Sometimes with input from engineers, sometimes not.
The problem is that building software merges the engineering (design/architecture) with the construction (coding/debugging/compiling/installation) that requires a rather broad range of skills. Most people working on small teams have to jump back and forth between the big picture (more engineeringy) to the small details (more constructiony) and back.
Finally, software development is no longer a new thing. People have been doing it for a half century. There are many people in the field today who's parents wrote software. And a few who's grandparents did it. It's high time we stop trying to draw analogies to building bridges or designing cars. Software Engineering is its own discipline with its own unique challenges and dynamics. Better to just take it for what it is.
http://infohost.nmt.edu/~al/cseet-paper.html
The results speak for themselves. A modern approach is Altran/Praxis Correct by Construction method getting nearly zero-defect software. Some provably correct in SPARK.
http://www.anthonyhall.org/c_by_c_secure_system.pdf
Today's work can add symbolic analysis (eg KLEE), assertion-driven test generation, languages like Haskell, certified compilers, and so on. What you're seeing in mainstream isn't software engineering. It exists, though, in s niche by academics and industry.
On the contrary, the worst thing was the abandoning of the sound engineering principles and the obligatory professional certification. Since then, the trade has been open to, and flooded by, know-nothing lazy half-wits who, should they find themselves in any other industry or medicine, would be immediately disqualified (or, rather, would not be admitted in the first place).
I work in product development, and when we send a product out for certification, they don't look at functionality, but only safety and regulatory conformity. Is the grounding wire green with a yellow stripe?
But it would be interesting for the rest of us to have an overview of how software is done in those fields. i'm guessing that those are also areas where litigators have refined their ability to prove fault and quantify injury in dollar terms.
In other words, web "devs" who are now calling themselves "full" "stack" and flood all walks of software development with subpar nonsense in the name of silly startup "mvp".
Or as beliefs of a customer about a product, or a company?
The skills to get to the latter are often different to the first, and both are expected in many modern software engineers. The later is much more about iteration, experimentation, empathy, and judgement. It's not worth polishing areas of a system which you anticipate abandoning based on the results of an A/B test. Writing it and abandoning some meh code can still be the best engineered choice in a product development process.
The engineering of the development process matters more than the program.
I'd argue that success in modern software engineering (at least in Silicon Valley) is at least as dependent on understanding social processes, as it is on computational ones.
At least where I'm from, telling people what I do, I often get labeled as a 'computer person'. You know, kind of estranged from the real world and not good for anything practical (and mind you, after college I shed a lot of my admittedly nerdy past). Coining the term 'software engineer', with the word 'engineer' being something people can relate to and see it as a respectable profession, I think can help a bit in conveying that yes, what we do is a real and important job and no, we are not all geeks that totally like to be in their dark basements all day.
Seeing as the 'recovery' seems to be largely move fast, break things, worry about security never. I'd rather software not have recovered.
Now that the agile wave has come and gone, we are doing sprint waterfalls.
A big part of Agile is "We think this story might take x, but it can take x+y divided across 2n weeks of progress." Unless you have a PMO that can say "Okay, fine, but we're giving you $x to do it, manage it yourself," you'll always fall into waterfall because y is too difficult to financially forecast.
Rally by CA Technologies "solved" for this and gained lots of enterprise adoption somewhat quickly by trying to tack dollars onto sprints; from what I've seen, this essentially created a "scrumerfall" culture where teams have sprints with stories ("functional requirements") that come from epics ("design requirements"), but everyone has tasks and they'd better be estimated with hours.
This is what "Lean Enterprise" tries to address. PMOs should use data from their customers to drive their projects, and projects should be "invested into" instead of "estimated and rationed."
Another reason to choose "engineering" was to encourage software practitioners to incorporate some of the proven methods of traditional engineering. As an example, I've noticed recently that ethics training is an established part of an Engineer's education, but not so with Software "Engineers".
There is always room to improve, and I don't think ignoring the past, and justifying that ignorance by saying "but we're different" is necessarily the best way to achieve that improvement.
See also: Big Ball of Mud http://www.laputan.org/mud/
I think agile methods can help a great deal in software development, especially breaking apart your projects into more discreet steps and supervising progress or giving the possibility for correcting course via sprints. Also using eXtreme programming tools like pair programming or code reviews to avoid having people slip into a frenzy of their individual domain / help keep obvious bugs out.
Having minimal time and changing requirements is just part of the game. And people "hacking" code together is just... well bad craftsmanship really
Standard engineering design breaks projects into discrete steps, it's not at all unique to Agile. Doing that is essentially the definition of "work breakdown structure". Course correction time is built into Gantt charts. Engineers have had to deal with constantly changing requirements, minimal development time, etc, since long before software came around. They're further constrained because you can't just ship a patch to a bridge or plane after it's completed. And yet they manage to get reliable, functional work done!
Agile is management with ADD. Waterfall is a straw man process that no competent organization would use.
Wtf does this even mean? What are you talking about. If the bugs are obvious why do you need extreme programming to find them? What frenzy? Extreme programming is garbage.
From my experience, when a proper preparation is done and there are clear requirements, most of the "obvious" bugs do not even come into play. They do, when "hacking" a feature and / or working in "POC" mode, which, for me, is not software engineering.
Because sometimes you've already worked for a few hours and end up wasting time with stuff, which could have been spotted easily by help of a fellow developer.
>What frenzy?
The frenzy I was talking about was referring to working on a project for weeks on end and forgetting that of course you are now skilled in that domain you built yourself, but at some point other people will have to work with that system too and for them your answers might not be that obvious. Having other people check in regularly can prevent this.
Also, please don't 'wtf' me, you could have just as easily framed your question in a more humane way. I wasn't trying to defend extreme programming here, but merely stating that a focus on open communication will lead to more robust software. Don't abuse my comment for furthering your personal hate against some software development methodology.
Sometimes nothing slows you down as much as being in too much of a hurry.
Incidentally, prototyping also gives rise to a very powerful idea of solving the problem by first creating a domain-specific language in which a solution to the problem can be expressed in the most natural way, and then providing an efficient implementation of that language.
Can anyone find that paper online? https://news.ycombinator.com/item?id=962733 points to a blog post about it from 2009, but none of the links seem to work.
Another well known project is CompCert the certified C compiler [1]. Which has seen a fair amount of external testing and use in verification of GCC and Clang as a reference for checking invalid compiled semantics [2] (to say nothing of compiling programs).
For me I think it started with reading a throwaway aside on "type verification" and wondering what the heck that was. After a bunch of wikipedia searching into different types of type calculus (not fun), I saw a reference on dependent types, which led me to Coq tutorials, and then, in an effort to be more practical, learning more Scala and a some Haskell - reasoning being that even if they didn't have full support for dependent types, at least their type systems were on the right track.
Failed efforts so far have been learning TLA+ and Idris (I haven't yet found a way to apply any learning), and trying to get through some youtube lectures on homotopic type theory. Maybe next time.
https://translate.google.com/translate?sl=pt&tl=en&js=y&prev...
Check the "Consolidation Units" and "Specialization Units" required for earning enough credits.
Also the degree is validated by the Portuguese Engineers Society.
Just one example, most Portuguese universities offer similar degrees.
I am a strong proponent of Engineering in our work and how software should be under the same quality regulations as other products in general, not only when there are human life's at stake.
Optimistically, the community might come around to realizing that this sort of development is at least desirable, and we might wind up with the basic components of popular OSes, or of firmware for dangerous things like cars and medical devices, being produced via provably-correct processes. But that will almost certainly wholly exhaust humanity's reserves of strongly math-educated software engineers (not to mention, industry's willingness to pay for it.) Software engineering may be impossible because most humans don't like math.
also on the other side there's a very limited pool of employers willing to pay for this kind of engineering - aereospace and military mostly, and the second is cheapening out too.
I would imagine the average natural gas power plant would have more than a few issues if you fed it pure hydrogen instead of whatever methane based mixture it was designed for. Similarly I imagine there are other inputs that have tight specification, lubricants, etc. But, these inputs are relatively easy to control via both human and machine processes and there aren't that many combinations you would consider valid. There are complex inputs in the physical world, for example the impact of complex wind patterns on bridges, but I still feel this is simpler than many input spaces in software.
In software, at some level you're almost always dealing with human or machine generated input which may be very complex in nature such that that even after all reasonable efforts at validation the size of the possible input space is massive if not effectively infinite. We're getting a bit better at dealing with this, e.g. fuzzing, but there is a long way to go for fuzzing to be viable on more complicated inputs.
Real "software engineering" would have to be done with Agda, Coq, or similarly tools - theorem provers that guarantee your solution is sound. It is done that way for software that absolutely cannot fail (eg, aerospatial). Unfortunately, engineers are usually too far away from this kind of knowledge - and sometimes even doubt this is possible or doable, even if you direct them to the research (!).
By the way, software engineering disciplines are usually depressive OO garbage, frameworks and all that non-sense that comes with excessive Java usage, and frankly never teach anything about reliability or even proving programs correct - which should have been their primary goal.
The nexus in products like vehicular and medical equipment software might be a good place to start implementing something like a discipline, but those systems are so much more singular and simplistic in their product function (not the implementation, I realize) than what the average software dev is working on that I don't even know how much downstream influence it would have.
There was a movement around 20 years ago to establish a field of practice called"software design" but I don't think it went anywhere. The problems remain the same though. http://hci.stanford.edu/publications/bds/
Maybe the two will meet at some point?
EDIT: I kind of got distracted by my JavaScript-story here. What I originally wanted to post was: I always had an interest in philosophy and the logical thinking you can apply in software development (i.e. you can save yourself so much trouble by just thinking through your system thoroughly before writing even one line of code). However, the general sentinment that I also got from a lot of my classmates was that they didn't want have anything to do 'with any of this stuff' (this stuff being the humanities) but didn't have a problem with cracking hard math problems day in day out (which for me at least is pretty similar to using logical thinking / philosophy on a less abstract level).
Sorry, if by my large edit I confused some people!
However - shipping code is often very organic, cultivated stuff which foils attempts to establish a coherent philosophy, a through-line of "how it works" - it works because it worked the day before, all that changed was that a little bit more was added. Repeat till broken, then refactor back to sanity(else abandon ship). It's Sisyphean hill-climbing.
This in turn plays into the falsity of "engineering" as a way to describe what's going on with a lot of code. You can do it in the small, but at any scale the codebase habitually becomes a living organism. And that tends to get puzzle-solver types excited, because if it's alive and keeps changing, then they will have endless problems to solve forever!
So I believe the philosophers of the crowd ultimately tend to move away from the applications coalface and look for something relatively smaller that does allow some time for reflection and distillation of the problem.
In my daily work I often refactor an old, organically grown, codebase and that's exactly what I do:
I try to think of what the system actually is / should be and try to put this in code in as concise terms as possible (that's kind of a general description of software development, but I just wanted to contrast this to simply hammering in the quickest fix you can think of). A lot of times I wonder though, if that is really what is needed and a simple 'prototyper' kind-of-developer would get the job done faster, although maybe not as elegantly (slight humble-brag there).
However, I absolutely agree that "systems thinking" is seriously underemphasized in undergrad. Bad algorithms are replaceable, bad systems require deeply invasive refactors.
for (auto p = thing.begin(); p != thing.end(); p++) { ... }
for anything that uses an iterator. No need to write out the whole iterator declaration for p. Or to generate it in a template. Suddenly, iterators became far easier to use.Since then, Go and Rust have taken a similar approach of limited type inference. There seems to be a developing consensus that function parameters should have explicitly declared types, but within a function, type information should be inferred when possible.
We're seeing convergence from the other direction, with Python and Javascript (via add-ons such as TypeScript) acquiring type declarations.
So the two are meeting.
[1] https://docs.python.org/3/library/typing.html
[3] https://en.m.wikipedia.org/wiki/TypeScript#Compatibility_wit...
You need additional tools for the type checking, and these tools are in experimental stage.
I've seen a diagram like this http://wordaligned.org/articles/programming-nirvana-plan-b but with more detail. ie. with C++, Java, Python placed in there somewhere.
(probably in the linked paper, but I can't access it now)
The problem is that outside of the tech industry, most of the people with the pursestrings don't see the value in it most of the time.
Why? When their main widget makes $100M/month (for example) and its current maintenance, as fucked as it might be, is ($1-10M/month, or 1-10% of revenue), spending, say, $1-10M to drop that OpEx to half of current costs, prima facie, doesn't make a lot of sense.
What changes that equation is when some event that could have been prevented by good software engineering principles occurs. Good examples: the latest British Airways IT meltdown, LinkedIn's massive data breach, 100's of millions of CC's stolen from TJ Maxx and others.
You see, when something happens that threatens that $100M/month cash machine, many millions of dollars of bonuses, raises, hirings, parties, expansion plans, and other general symbols of growth and progress get threatened as well. THAT is what people with the pursestrings don't want to see messed with.
The overall problem with this is that it's reactionary, and every reactive event has a lifespan. So it takes finding someone with connections that can either capitalize on an unfortunate event trailblazing a path for software engineering to happen, or tell a good enough story of previous woes and misfortunes that can make that happen all the same.
TL;DR: If you want "software engineering" to happen at your company, you need to tell a compelling story of how NOT making it happen will lead to great losses, and then you need to be extremely patient.
People think this is a tool problem but it's not. Well, the tools kinda suck but fundamentally it comes down to culture and mind share. My background is in pure math but my programming knowledge is steeped in the hacker tradition. It is very hard to overcome all that momentum.
There is no killer application for verified programming. Whatever you can do in Coq in 10 weeks you can do in C in 2 weeks with a few extra buffer overflows. We'd be further ahead if there were more tools for validating C code than trying to rewrite the world with dependent types.
No offense to you, but often times people that entered programming originally from related fields and taught it themselves give me the hardest time working with them. Like, no it's not enough that the software now provides this feature but you copied 1.200 lines of code for it and just squeezed in your if-branches and for-loops somewhere. Your job as software developer is to think how you can wed these new requirements with our existing codebase for others to build upon.
Sorry, but as a bit of a pedantic and clean code enthusiast this is kind of a passion topic for me :)
That said, I tend to disagree with people who believe the solution is "use Coq".
From Webster's (1948):
engineer: n. 1. [Rare], a person who makes engines. 2. a person skilled or occupied in some branch of engineering: as, a mechanical engineer, an electrical engineer. 3. the operator of an engine; especially the driver of a railroad locomotive. 4. in military science, a member of that branch of the army which is concerned with the construction and demolition of bridges, roads, and fortifications, the laying and sapping of mines, etc. Abbreviated E., e., eng., engin., engr. v.t. 1. to plan, construct, or manage as an engineer; hence, 2. to contrive; manage skillfully; superintend; guide (a measure, action, etc. through).
engineering: n. 1. the planning, designing, construction, or management of machinery, roads, bridges, buildings, fortifications, waterways, etc.; science, profession, or work of an engineer: abbreviated E., e., eng., egin. 2. a maneuvering or managing.
The problem for software engineering is that it's really easy to model with code, and those have an unfortunate tendency to become the final deliverable. We have short iteration cycles that make the benefit of systems engineering and large scale engineering approaches less obvious. In CE it's quite obvious that a model of a bridge (physical and mathematical) will have benefits towards the final bridge construction. It's also obvious that it's just a model.
In software, the model is too often functional enough to pass as the real thing, but fails in various ways: was constructed haphazardly allowing security vulnerabilities or stability issues; wasn't designed to run quickly or to scale effectively across multiple servers; not developed with maintenance in mind.
Fundamentally writing software is about constructing a formal description of a less formal 'specification' of some abstract system. The architecture used in this formal description corresponds to what's ordinarily called theory in science; when we execute our formal description, it corresponds to performing an experiment, which may lead to the discovery of anomalies (i.e. bugs); sometimes this anomalies are significant enough that we are led to revise our theory (i.e. refactor); if our re-conceptualization is significant enough and we decide to use an entirely new architecture, you're running into something like a paradigm shift with all the resistance and other social factors this entails.
Coming up with architectures/theories to formally describe systems in the foregoing manner is not something we know how to make reliable. If that goal is clearly out of sight for science, why would we expect it to be achievable in the sibling discipline of software development?
The exception I see to this (which admittedly is a large exception) is that probably most programs which need to be written are very similar to programs that have been written before. If we had some unified process for evaluating architectures for these programs that we write over and over again, then we'll probably eventually stabilize on 'true' architecture for that class of program. We see this happening to some extent in the creation of 'frameworks,' but maybe we could come up with better measures for comparing the aptitude of competing frameworks?
It could also be that the physical systems described in science tend to be amenable to simpler description than typical software specs, which may be loaded with non-unifiable features. In this case, frameworks/theory only gets you so far because most of the work is just describing special cases...
Anyway, maybe the 'science' we need to found software engineering on top of would be a formalization of this general process of coming up with formal descriptions of either abstract or physical systems. I bet there are regularities there which could be exploited for systematization—and if nothing else, there are likely insights to be had just through seeing software development as a thing having significant relations to the practice of science.
Edit: phrasing, typos, etc.
Good luck!
- Incompetent management.
- Incompetent programmers.
Incompetent management, because of "sky is the limit" effect, pretending being some Steve Jobs or similar, instead of doing the real job: risk management, resource allocation, put competent people in charge, and being involved so the project(s) are kept on track.
Incompetent programmers, because not being qualified enough, not having enough experience, or whatever reason making the programmer being nihilist, or a mix of the previous.
If you have competent management, and competent programmers, you'll deliver. The problem comes when you have incompetent and/or corrupt management, taking decisions from emotional impulses, or thinking in their bonus first, hiding the problems, and making the "bomb" explode years later. Then, you'll receive the excuses: legacy code, architectural problems, blaming contractors, processes, methodology, etc.