Flow-Design – Bringing serenity (back) to programming
Software development has become stressful, confusing, conflict laden. That’s hurting the producers of software as well as its customers and consumers. Flow-Design is an approach to software development trying to change this. It’s no silver bullet – but still it can make your life as a programmer easier in a lot of respects. Maybe programming can even become fun again? See for yourself…
Why has software development become stressful? There are of course a lot of reasons and Flow-Design cannot possibly tackle them all. But one of the reasons is that new paradigms and practices have entered programming – and the honorable technique of thinking before coding has left it – to a considerable and notable extent in many real world software development efforts at least.
A lot of programmers are technology driven and reliant on pre-fab snippets they’re trying to harvest from internet sources. They’re betting on features of their programming languages and underlying frameworks based on paradigms that have become mainstream – without the support of a systematic method guiding their work. It’s hard for them to think up solutions without poring over their keyboard; it’s even harder for them to co-create solutions with others. Communication is rampant, but real collaboration is rare. Sadly many so called teams are just groups of coworking full stack developers.
This is where Flow-Design comes it.
Flow-Design is a comprehensive eclectic approach to systematically move through the process of transforming requirements into working software – and beyond.
Flow-Design is eager to help increasing the longterm productivity of your team!
Flow-Design weds thinking before coding to clean code. It’s about visualizing solutions before implementation as well as mapping them to code. It invites collaboration at the whiteboard as well as in the IDE.
Flow-Design has its roots in many existing methods and paradigms but does not simply rehash them. It’s a pragmatic mixture to bring to your mind and to your fingertips what works. It distills, explains, clarifies what often is misunderstood or taken at face value and implemented as a cargo cult.
That said it is not dogmatic nor does it claim to be the one and only way to programming bliss. But it has helped hundreds, maybe even thousands of developers over the past years to get a better grip on programming.
Flow-Design is not so much about replacing but rather about complementing. It fills gaps left by other approaches. However, there are some aspects of “programming lore” it challenges. And why not?
Come on, give it a try…
The journey is the reward
Flow-Design is about a journey. It’s a journey that seems to have been forgotten in the past decades. Every developer is going on this journey every day, but strangely it’s often not done with joyful anticipation, but dread. Or it’s done in a very unconscious manner which then leads to all sorts of unpleasant effects.
Flow-Design wants to change that. It wants to put the fun back into travelling from requirements to code. Flow-Design calls this bridging the requirements-logic gap.
The journey from requirements to working code has three legs:
Flow-Design posits that good software development starts with a good grip on the requirements. Otherwise it’s garbage in, garbage out.
This is not new and much has been written about Requirements Engineering and describing requirements with Use Cases or “seeding” coding with User Stories.
Flow-Design embraces these approaches – but dares to add to them a twist or a missing ingredient: systematic slicing until requirements are in a shape fit to be processed by the next phase.
Can you describe a solution without or even prior to coding? That’s what the “lost art” of designing software is about.
Flow-Design also posits that good software development has an explicit design phase which precedes coding. During design a plan or a model of the software is created on a considerable higher level of abstraction than code in mainstream programming languages.
Explicit design is about speed and creativity and collaboration.
Thinking before coding by visualizing this thinking makes mental models easier to develop and share in your team.
Fear not, Flow-Design is not “UML redux”! It recommends a light-weight visual notation tailored to the needs of developers in the trenches. And it embraces many other thinking tools – as long as they fulfill certain conditions.
Although Flow-Design emphasizes explicit thinking in general and explicit design in particular it’s adamant in its demand for a straightforward mapping of any design to clean code.
Flow-Design adds a few principles to the commonly known ones like SOLID to improve testability and malleability of code.
And it’s firm in its stance on test-first development. TDD, however, is embraces as just one of several variants of a test-first approach. It’s called “classical TDD” and other variants are added to be used in situations of different complexity.
Flow-Design is highly visual. It tries to appeal to the right side of your programmer brain to balance out the usual focus on text. Also it tries to jog your creativity as well as meaningful communication in a team, that’s why diagrams are (usually) drawn by hand on a flipchart or whiteboard (but if you like, you can use a whiteboard app on your iPad, too).
Flow-Design diagrams might look messy – but that’s a feature. The seeming messiness allows the brain too attach more emotions to the visualizations which is supposed to increase retention and involvement.
They are individual “graphic recordings” of an unfolding person’s or team’s thinking process. Don’t confuse them with documentation! Flow-Design is not about documentation for 3rd parties like managers or clients. It produces “for your eyes only” ad hoc logs of a creative process to be of temporary value for those who were involved. Nothing more, nothing less.
As soon as visualizations get drawn accurately with a tool like Visio they lose their fluidity. They become “huen in stone”, hard to change, fixed, and taken as they are. That’s the opposite of responsiveness to changing situations. Although explicit design is about creating a plan – which makes some developers cringe –, this plan is not thought of as being fixed in a particular way. Rather it’s a snapshot of a current idea of how code should be structured – until some premise changes.
Flow-Design is trying really hard to make your journey from the moment you’re presented with requirements until you’re done with coding as pleasurable as possible. Take Flow-Design seriously in its slogan Bringing serenity to programming. Think “liberation” from much hardship.
Actually, Flow-Design was born out of suffering. The emotion is all started with was fear: fear of the empty flipchart. How to start software development when the flipchart is blank and there are no code files yet? Which steps to take first? How to solve the problem and – as an object-oriented programmer – find the right classes. That can be daunting questions.
Flow-Design is here to help ending the suffering from the fear of the empty flipchart. To understand how, it’s important to start at the beginning. You must become very clear about what software development is at the core. What’s the most wanted thing a programmer should produces?
Sure, customers want you to produce code, but not just any code. Not even clean code. Just part of your code is what customers and users are craving for. But what part is that…?
It all starts with logic
Flow-Design starts by moving your attention to what it calls logic.
Logic is the behavior creating part of your code.
This is a C# example of “pure” logic. The behavior it creates is that of a Hello-World program “on steroids”. Here’s a brief description of what it’s supposed to do (aka the requirements):
Whenever the program is run it asks the user for his/her name and displays an personalized greeting. But not only that, it also varies the greeting according to the number of times the user has identified him/herself. For that the program keeps a persistent record of all user names.
If you like think of logic as being the statements in your code. Or to be more concrete it’s:
- operators, e.g. >, &&
- control structures, e.g. if, while
- calls to functions of the languages standard library and all other libraries added to a software, e.g. File.AppendAllLines(), string.Compare().
More generally one could say it’s all the function calls to code you only use as a black box. This code forms a “substrate” you’re building your code on. It’s like a software specific virtual machine.
Of course there exists other code than logic. In C# for example you cannot even get a program to run with more code wrapped around logic like the above, i.e. a class and a function (usually called Main()). Other languages, however, allow programs with just pure logic, e.g. Python.
To single out logic like this is not to say other code was not important. Of course not! But Flow-Design deems it important to very clearly understand that non-logic code is not strictly necessary to satisfy users and customers. Because only logic creates the behavior as described in the requirements.
More formally: Behavior is what code does when you run it and give it input to process: it produces output and possibly changes some state. Behavior is created by functionality with a certain performance and other non-functional qualities.
Behavior is the sum of all runtime qualities of software.
From this follows you need to have a very, very good reasons to write any other code beyond the necessary logic. But Flow-Design will provide you with these reasons.
In addition, even before that conclusion you need to realize something else: It’s very, very hard to come up with suitable logic in the first place. Look at the above logic and ask yourself: Given the brief description of the requirements underlying it, how long would it have taken you (in your programming language) to come up with such logic? One minute, two, 10 minutes? And would you be able to type it in and immediately run it without any flaws? Usually even experienced developers cannot do that right away and without some trial-and-error. And that’s the point! If such a trivial problem does let pop-up in your head the required logic, then what about more difficult problems?
Finding logic and getting this logic right is no small feat. You deserve all help you can get to make that easier for you. (Beyond Google and Stack Overflow, that is.)
Enter Flow-Design with it’s notion of bridging the requirements-logic gap.
But wait, there is more! Before you can make your first step onto the bridge you need to become clear about one other foundational concept…
“Do your work, then step back. The only path to serenity.”
Make logic accessible with functions
Logic is all that’s needed to make users and customers happy. At least for now. But in order to get logic at the fingertips of users there is one more thing needed: a wrapper. This wrapper are functions.
Logic is made accessibly through functions.
In the most simple case that’s the Main() function of many languages. It defines the entry point into your logic. It’s the only required function. But in addition you will want to wrap your logic into more functions for at least two reasons:
- to be able to easily use it (call it), maybe even in multiple places
- to state its purpose clearly.
An example of a function to make logic accessible. Without Main() the execution of the logic could not be started by the user. (This is at least a convention of some, but not all programming languages.)
An example of a function which assigns a purpose to some lines of logic. It wraps them up in a block, abstracts from the individual statements by hiding them begin a label (function name), and clearly defines how to use this “package” (what to provide as input, and what to expect as output).
With “a wrapper” logic can easily be (re)used.
Functions are very useful. Writing non-trivial programs without functions is (near to) impossible.
But make not mistake, functions are still not what the customers wants. At least not consciously, not knowingly. That’s why functions don’t show up in most requirements. Customers are willing to pay for functionality and efficiency (e.g. performance, security, usability).
So the big questions is: why more functions beyond the bare necessity?
The third requirements category: longterm productivity
Customers primarily want the software they are buying to function efficiently. They want to be able to press a button and get some calculation done quickly. Easy and fast.
At least that’s what they want from the software. Because there is more they want for their money. But that’s a requirement not of the software, but of the production of that software. Customers also want productivity. They want the producers of software to be responsive to their needs, they want them to deliver quickly on ever changing requirements. You sure are experiencing this demand, too, in your daily work as a programmer.
Surprisingly, though, this requirement is not really expressed clearly by customers. It’s an implicit requirement. Meeting this requirement is taken for granted. But since that’s not happening more often than not, small and large conflicts ensue.
One of Flow-Designs goals is to bring this to the attention of all stakeholders of software development.
But not just any productivity! It’s longterm productivity. Because short term productivity trivially is already in the focus of managers and customers.
Customers want producers of software to show longterm high productivity.
Nobody wants software teams to be highly productive in the beginning and then slow down and finally grind to an unexpected halt. Sure not! But actually that’s what’s happening in many projects. The reason: longterm productivity is not really on the radar of customers as well as management. Longterm productivity is not part of a legal contract, it’s not part of the development organization’s culture.
Flow-Design has found that mainstream Agility unfortunately also has not delivered on this requirement. It’s still the elephant in the room: Nobody is really talking about it, but all fear if they started to it would mean to leave their comfort zones.
A lot of suggestions for how to tackle the problem of longterm high productivity are lying on the table, though. But a systematic approach as to how to integrate them is lacking. This is where Flow-Design is trying to help.
Flow-Design not only wants to make it easier to “flow” from requirements to working code once, but many times.
Flow-Design is about delivering not only working software, but clean working software, i.e. working software that’s easy to change.
It’s completely in line with the Agile Manifesto by providing guidance to become responsive to change and improve the interactions between individuals (e.g. customer:developer, developer:developer).
“But I do know focusing on the exterior doesn’t make me happy.
If I want peace and serenity, it won’t be reached by getting thinner or fatter.”
Inner quality attributes for high longterm productivity
If you’ve been interested in clean code development in the past, then Flow-Design is for you. In fact Flow-Design is what long discussions and deliberations about clean code have evolved into. Clean code is a great start – but it’s not enough.
Longterm high productivity is the purpose behind clean code, even though that maybe hasn’t been made very clear in the seminal book “Clean Code” by Robert C. Martin. Clean code is “just” a means to that end. And that’s important to know because otherwise it’s hard to “sell” clean code or other software craftsman habits to management.
Now, if longterm high productivity is the purpose, how can it be lived up to? What can be done about it? How would code and a software development process look different if they’d support this purpose? It’s not about the exterior of software, its behavior, it’s about the inside. “Inner qualities” need to be increased.
Flow-Design (so far) has identified three areas to bring more attention to:
A sustained high level of productivity cannot be reached if software is not as bug free as feasible.
Customers are willing to pay for genuinely new/improved behavior, not for fixing buggy behavior. Managers want to pay salaries for implementing new/improved behavior, not for fixing buggy behavior.
Fixing a bug steals capacity from the team to add to or improve code genuinely. Lurking bugs have the potential to disrupt development at any time, to make teams unreliable, to create conflict.
Delivering working software that deserves its name means delivering bug free software.
Correctness eliminates the waste of repetition.
Longterm productivity can only be high if software can easily be adapted to ever changing requirements.
Software exists in a co-evolution with its stakeholders (above all its users). If it’s a success they want more. If this demand can be satisfied quickly and smoothly then the success of the software becomes even bigger.
Malleability, changeability, flexibility are thus core attributes of code (as well as its development team).
Evolvability eliminates the waste of hesitation.
It’s a truism that customers/users hardly ever really know what they want. They need to see before they know. But what can be shown if it’s not known what’s needed?
This deadlock needs to be broken by more preliminarity or tentativeness in programming.
Iterative and incremental development is just a start. More is needed to protect the correctness and evolvability in the face of uncertainty.
Preliminarity eliminates the waste of deviation.
Five questions towards high longterm productivity
Flow-Design strives to increase the quality in all three aspects by constantly asking:
- Maturity: Is the code already correct? How do you know and are there automated tests for that?
- Freedom from regressions: Is the code still correct? How do you know and are there automated tests for that?
- Understandability: Can you easily reason about the solution, develop and share mental models as well as code? Reason: Applying changes to improve code requires to first understand it in order to identify the best places to apply modifications to.
- Testablility: Can the code be easily tested if need be? Reason: If code is testable then the need for time consuming debugging decreases and correctness is easily verifiable. Also testability is a function of modularity which is an accepted attribute of evolvability. But testability seems more tangible.
- Prototypes: Is it easy to gain feedback using prototypes of all sorts and sizes? Reason: Prototypes protect production code from “churn”. Production code should only be changed if requirements are unambiguous.
Production code: Code to create value for the customer/user, covered by automated tests
Prototype code: Code to gather feedback for the developer and to create insights for the customer/user, not covered by automated tests.
Functions at the root of high longterm productivity
A lofty goal, some non-trivial goals is what Flow-Design is about. But how to achieve all this? What can you do as a mere mortal programmer?
That’s where functions enter the picture! Functions wrapping logic are at the root of Flow-Design. That’s from where all else is growing.
- Functions help correctness: Only through functions you can test logic automatically with a test framework. Functions clearly define how to pass test data to logic and how to receive any results.
- Functions help testability: Logic not wrapped in a function by itself cannot be tested in isolation. If you want to increase testability you need to bundle-up logic into smaller “function packages”. This also improves the potential for “mix and match”, i.e. to create new behavior by re-using logic in different contexts.
- Functions help prototyping: There are many kinds of prototypes you can do, e.g. paper prototypes for user interfaces. But when it comes to executable prototypes functions make them possible. Because with functions you define points of variation in your code. Where there is a function call, different functions could be used at runtime (polymorphism). And logic wrapped in a function can be made accessible in isolation using special test beds. That way alternative solutions – small and large – can be presented for feedback.
- Functions help understandability: Functions assign meaning to logic through their name and the names and structure of parameters as well as result type. They increase the level of abstraction of your code which makes for easier reading. Compare the above pure logic to greet users with the code below. Which solution is easier to understand? With functions code can almost become prose you’re happy to read by your fireplace.
As you see, there are a lot of good reasons for writing more code than just logic. Functions are invaluable! In fact, Flow-Design values them so highly it could be called “function-oriented programming” (not Functional Programming, though, mind you!).
So the question now is: How to come up with the right functions for the logic needed? And also: What about classes? Or more generally: What about modules?
But first functions. Again.
Analysis: How to derive functions from requirements
Now is the time to take a first step onto the bridge over the requirements-logic gap. The first pillar it is supported by is the analysis phase of software development.
You sure have read about requirements analysis before. There are lots of ways to do it. Flow-Design embraces them all like doing formal requirements engineering, or drawing use case diagrams, or writing user stories. That’s all fine with Flow-Design, because Flow-Design stakes a complementary stance. It’s not replacing any of this, but adding to it.
Flow-Design has identified something lacking in these approaches. Something which makes it hard for developers to take the next step after analysis. What’s missing is specificity or tangibility. And that is not only causing trouble for software developers, but also for the whole software development effort. Lack of specificity leads to a transformation of requirements into code which suffers from the garbage-in-garbage-out effect.
Agility is trying to compensate for that by progressing iteratively. But unfortunately that can only compensate so much uncertainty, especially as long as uncertainty is responded to by modifying production code.
Flow-Design is trying to help with a two pronged approach:
- analysis of requirements guided by a incremental decomposition framework tangible and relevant to users as well as developers, and
- encoding of analysis results in a simple, yet formal way relevant to users and tangible for developers as a starting point for the design phase.
The purpose of analysis is in creating understanding.
But what form does understanding have? Is it just a matter of thoughts circling in your head? No, understanding needs to manifest itself simply and unambiguously for easy sharing and quick feedback.
Slicing – Decomposing requirements for developers
Slicing is what Flow-Design calls decomposing requirements into ever finer increments – which are tangible for the user and at the same time map to artefacts relevant to you as a software developer.
Flow-Design argues that a close collaboration between user (or product owner) and developers can and should lead to insights into the basic structure of a software system without starting into design yet. This way the structure is driven in an outside-in manner by starting from “trigger points” through which the user interacts with the code.
Those “trigger points” cannot just be enumerated by asking the user (or any stakeholder), though. They need to be uncovered during an exploratory process decomposing comprehensive requirements into ever more fine grained requirements along technically relevant “milestones”.
The first slice helps you build and clarify your understanding on the overall system level. All the software to be written is taken into view. It’s “put into perspective” with regard to the environment it is supposed to be working in.
The system itself is drawn as a larger circle with a smaller circle inside. The smaller circle is a constant reminder that the most important part of a system is its domain logic. And the larger circle emphasises the need for a boundary “protecting” the domain.
“Protection” is needed against pecularities of and changes in the environment which should not all necessarily affect the domain. Also the boundary enables and controls access to the domain.
On the system level code has to deal with to “forces” in the environment: users (orange, blue in above drawing) who want to control the system for their own benefit, and resources (violet) which the system wants to control for its own benefit.
Users depend on the system to do their work, the system depends on resources to do its work.
Flow-Design deems it very important to know early on who uses the system and what the system needs. These relationships are foundational because they drive technology decisions, affect paradigms used, or have an effect on interfaces and APIs.
Users are represented in their different roles/personas and basic usage scenarios (e.g. online, mobile, offline, desktop). They mostly are human, but also other software systems using the one under analysis are represented as users if they exert control.
Resources are always technical systems ranging from other software systems (e.g. an ERP system or Twitter) to hardware (e.g. printer, camera).
The system slice (or system-environment view; see also System context diagram) gives orientation to customers and developers alike. It’s the first “map” of the system to be drawn while discussing requirements.
The second level in the hierarchy of slices are applications. The question behind it is: Should a software system consist of just one “program” to interact with or maybe several, complementary ones.
Think of Microsoft Outlook as one application on your desktop to provide many different services: it’s an email client and a calendar and a task planner and a notepad. It’s one application to fit many “productivity needs”. But why weld together all those different services in one application? Is that the only or even best way to provide them?
Look on your smartphone to see an alternative way to do it: You are probably using different apps (short for “application”) for such different services. There’s an email app (e.g. Spark) and a calendar app (e.g. BusyCal) and a task planning app (e.g. Wunderlist) and a note taking app (e.g. Evernote). Different services delivered by different applications from different vendors.
Whether to implement requirements in just one application or maybe several is what the application level is about. It suggests to look closely at the users of a software system and imagine how to cater best to their needs. Maybe users are best supported by different user interfaces, e.g. textual or graphical, or user interface technologies, e.g. fat client or web client? Maybe users are best supported by different devices, e.g. desktop computer or mobile device.
The application slice suggests to think about a software system as an application suite – where the applications can be connected through shared resources, e.g. a common database.
The types of users of a software system are usually the driving force behind a decomposition into applications.
Applications are about usability and platforms.
Co-working is about different operating system processes working together to deliver application behavior. Co-workers are distributed parts of applications, e.g. clients and servers. But don’t try to anticipate a detailed architecture during this slicing step! Just a rough structure of the pretty obvious is needed.
Get a feeling for how applications cooperate: is it through shared resources or shared processes encapsulating resource access? For applications the driving force were users, for co-workers it’s resources.
Since the customer/produce owner is present during analysis slicing applications into co-workers is supposed to give him a more accurate idea of the overall structure/complexity of a software system early on.
Since resource access is the starting point for decomposition into co-workers this slice is about meeting efficiency requirements. Which aspects of an application’s behavior can be improved by distributing logic across operating system processes?
Please note: Applications as well as co-workers are depicted using the same symbol as for the whole software system. Flow-Design does that on purpose. That way the fundamental self-similarity of software on different levels of abstraction is emphasised: A whole software system has users and uses resources and revolves around a core of domain logic shielded from the environment by a clear boundary. And the same is true for an application as part of a software system as well as co-workers as individual operating system processes inside of applications. They are all autonomous parts with the same basic structure – just on different levels of abstraction or of different granularity. Flow-Design calls them software cells (see below for details).
The whole point of assembling users around a software system, decomposing it into applications and then slicing them into co-workers, is to compile a list of interfaces: Who wants to access which operating system process by what means?
To incrementally design and then implement software the process has to be driven from the outside in. First the demand has to be stated very clearly. With demand being whatever messages reach an operating system process from its environment to trigger behavior.
But how do messages “hit” an operating system process? Flow-Design views an user interface as consisting of dialogs. Each dialog is responsible for accepting certain messages, and which dialogs are available to users might depend on how previous messages got processed.
With co-workers on the table analysis continues by decomposing the relationship between co-workers and their users into dialogs. Regardless whether users are human or other co-workers/software systems communication happens through dialogs.
You can think of a dialog as a rectangular window/form in a fat client GUI, a page in a web UI, or a REST controller. All are offering some way to send messages to a co-worker.
On the dialogs level the question is: Which dialogs are there per user and how are they connected (if at all)? The above diagram shows a user sending a message to an initial dialog (rectangle). This might lead to a co-worker termination or a transition to another dialog presentation and so on…
Dialogs are about partitioning the user interfaces into smaller units with focused responsibility. Each dialog is qualified to accept only certain types of messages.
How exactly can a user interact with a co-worker through a dialog? That’s what the interaction slice is about: it compiles all the different ways behavior can be triggered.
On a REST controller as a dialog that’s easy: all published functions are “triggerpoints”.
But on a visual dialog, e.g. a desktop form or web page, there are many different ways to trigger the execution of logic. A user could press a button, click a menu item, enter a character, make a gesture, or move the mouse. In addition default interactions are opening and closing dialogs.
Sometimes triggering behavior through a visual dialog leads to another dialog being opened or the application terminating. Many interactions keep the control in the same dialog, though.
The very purpose of decomposing a system into ever finer slices even is, to finally arrive at individual interactions. Although slicing provides the basis for incremental development to increase the feedback from users and other stakeholder, its main purpose is to find the entry point functions for a system.
Flow-Design posits that design and implementation are much easier when starting from concrete functions. Even more so if these functions directly deliver value to a user – as those functions do which get triggered by a user.
Message Handling slices
Once the interactions with a specific dialog or a system in general have been found, each gets refined into its incoming and outgoing parts.
The image above shows the entry point function (or trigger function) as a circle. It’s called “H” for message Handler because it’s processing a certain incoming message send by the user via the dialog.
The message it handles is called “I” for Input in the image. With the help of the dialog (rectangle in the left) the user “shapes” an input message. It’s send to the message handler which transforms it into one output or another. In the above image that’s messages “O1” and “O2” for Output. An output message then flows into some receiving dialog (rectangles on the right) which might be the same as the one on the left side or not.
As soon as the input message of an interaction and it’s output messages have been defined, the message handling function is obvious.
This might sound a bit technical and not fit for customer’s/product owner’s eyes, but in reality there is no need to dive into such details. Talking with users about “the data they want to enter” or “the data they want to get displayed as a result” will deliver enough hints without burdening them with jargon or technicalities.
“Communication will bring understanding and understanding will cause
harmonious mutual relationships which can establish peace and stability.“
Uncovering the message handling functions a software system needs to create its behavior upon a stimulur from a user is the first step to understanding what it’s supposed to do. Flow-Design embraces Use Cases or User Stories and other methods to gather and manage requirements. But in the end all such documents should be mapped to the hierarchy of slices described above.
That way requirements become truly tangible for developers, because each slice has a corresponding programming artifact, e.g. operating system process (aka programming language project or executable) for systems/applications/co-workers or class for dialog or function/method for interaction and message handler.
What’s a User Story in code? How to represent a Use Case diagram with a programming language? With slices the mapping of requirements to code has already been done. That’s a solid base from which to continue with design.
Flow-Design has observed that many if not most developers find it easy to produce a solution when given a specific function signature to put their logic behind. Any coding dojo tackling one of the usual code katas starts with a simple function definiton (or one that can easily be derived from a problem statement).
With slicing this inclination is taken as the goal to achieve during analysis. Less tangibility than a list of functions belonging to a number of clear cut classes in well-known executables is bound to make design and implementation harder than necessary.