[ art / civ / cult / cyb / diy / drg / feels / layer / lit / λ / q / r / sci / sec / tech / w / zzz ] archive provided by lainchan.jp

lainchan archive - /λ/ - 9922



File: 1443441224533.png (9.79 KB, 300x188, handover.png)

No.9922

Certain things are easy to program, because the problem is well understood beforehand, or it's easy to split it up into parts that have little or nothing to do with each other, or maybe it's programming for fun and the final result is not that important.

But some times, ...there is a sense of need to completely design the system beforehand, to cover the edge cases without needing to redo half the program halfway in, because it needs to be able to do that thing as well, which changes everything. To get it right from the very start. Even if Murphy ensures the futility of that goal.



I tend to draw/write sketches that I never read, which is just as well, because they are never documented enough that they make sense, should I actually forget what I was thinking at the time.
A lot of the time it seems like I redraw the same sketches with few or no changes each time I think of the problem.



How do you do it?

  No.9924

When I write apps in frameworks that use MVC i tend to design the model first, then the view. By that time I typically have a pretty good idea of how I will write the controller. Regardless of what frameworks you use, designing your data structures first is likely a good idea

  No.9925

I've taken a few programming courses in college and I'm not too sure if this is implemented IRL as much, but in all my courses each project/assignment was done in two parts. First and always first you would have to outline your program in a flowchart-type design. Similar to UML in a way. It was so annoying at first that I would just code first, then create a flowchart depicting the structure of my code, and my instructor would catch this and get pissed. There are multiple programs you can use for "flowchart programming", its pretty self explanatory. I'm not sure if this is the answer you were looking for.

  No.9926

File: 1443446584845.png (176.26 KB, 186x200, 4ce5574869bc17ee8f254b770523a565a9d81695.jpg)

I program in Common Lisp.

  No.9929

I often draw sketches, and I seldom read them as well. But I also use the power of lisp, which allows me to design the system I'm making interactively, one sexpr at a time, and as I do that my understanding of what I'm trying to do grows, I can then just recycle some part, redefine others, or refactor the whole thing. I usually make functions small and concise so it's easy to mix&match, I usually end up with lots of open files with the working program distributed among them between discarded versions of functions, and I then try to find which ones I finally used

  No.9933

>>9922
Well, last time I had a problem which was tricky enough that a "I sit down and start programming"-approach was not sufficient, I took out some paper and started writing.
Real programming (not code pissing) is done more or less like math : On paper.
There is yet no better interface for tinkering with ideas than a pen and paper.

  No.9945

Nobody should start to undertake a large project. You start with a small trivial project, and you should never expect it to get large. If you do, you'll just overdesign and generally think it is more important than it likely is at that stage. Or worse, you might be scared away by the sheer size of the work you envision. So start small, and think about the details. Don't think about some big picture and fancy design. If it doesn't solve some fairly immediate need, it's almost certainly over-designed. And don't expect people to jump in and help you. That's not how these things work. You need to get something half-way useful first, and then others will say "hey, that almost works for me", and they'll get involved in the project.

  No.9946

When writing personal projects in C++ or similar languages, I had to do what OP described to make any progress at all, and even then, I always failed to complete my projects. I should note I'm not a professional programmer, so I don't have any incentive to complete those projects aside from my own will.

Since I started using functional languages, I've had much more success trying to do the things I want to do. I've found it easier to design the program as I write it, since the end goal is to have a single function which does what I want it to.

I might need to write down the inputs and outputs for my functions, but I think the program will explain itself as you build around the inputs (or outputs). I also write down protocols and specifications if I'm writing something like a server.

  No.9954

I haven't really worked on any super large projects. However, when I'm working on a more difficult one I usually will break out the pen. Jot down what I'm trying to do and how I'm going about it.

  No.9957

The pondering I had in my mind when posting the question was more along the line of how do you figure out what to draw? how do you know the model you write a representation is a good model? even if I didn't manage to word it very well, ...because that is a very cloudy thing for me personally. But maybe I'm not alone in having difficulty to answer that, it's basically the question of "what do you do in order to form thoughts?" to some extent.

  No.9958

>>9922
I usually do the same thing, diagrams that I end up throwing out afterwards because they're embarrassingly bad and my code is already a better representation of what I was trying to do than my original incomprehensible scribble.

>>9957
If the program requires stored input (like from a file) rather than just user input, I'll usually draw how I imagine the data should be structured.
For both cases I'll draw a flowchart of the main program, describing roughly the steps, and then for each complicated step that requires it I'll write a separate flowchart.
This way, I can write each separate flowchart as a function and call them from the main program in the order I've written down, and soykaf should start working pretty quickly.

  No.9963

>>9922
You think you just have to try and err. That's mostly how people came up with solutions for now well understood problems in the first place. They call the process research or experimenting and generally don't expect to get tangible results out of it, but rather learn about the intricacies of the problem and how to tackle them in later iterations.

I recently read through some 20+ year old documentation for X11 and it was interesting to me, because the authors laid strong emphasis on how certain problems like internationalization are not yet well understood and the presented solutions therefore preliminary. I guess planning only gets you so far, eventually you'll need to put your theories to the test and draw the right conclusions from it. Otherwise you just end up sketching the exact same diagrams over and over again, as you said.

  No.10403

>>9957
I've found that explaining the problem to others helps.

  No.11867

>>9922
The language you use really affects your thinking.

I like Forth and bottom up design. I always try to think about what primitives I'll need for my program. Then I can program everything else before I even define the primitives.

Forth also heavily encourages factoring. If my word's definition won't fit on a single line, I'm probably doing something wrong.

  No.11883

>>11867
lisp is the opposite. I think about exactly what needs to happen for my program to work, and I program all the components for that, and the components for that, and so on.

  No.11885

>>11883
I'm also a fan of Lisp and I feel that it's very similar to Forth.

  No.13246

This topic warrants more discussion.

  No.13249

I try to think about how the data in my program will be structured.

Once you have that, it's much easier to think about how to treat the data.

  No.14244


  No.14247

>>11883
>>11885
I mostly program in lisp, and if I'm designing a larger system I'll typically sketch out how the larger system is supposed to work. Sometimes I do that in lisp, but the code I write typically doesn't make it into the final product. For each subsection of the project, though, I typically start from the top down.

I should note I haven't written anything specifically useful in common lisp yet (though I have in scheme); though I'm finding that as I improve and understand the programming abstractions and syntax better I need less and less abstract design to get something working.

  No.15068

i use a kind of UML-like system that i made up myself because i can't be arsed to do it professionally.
along with that i write pseudocode,
and eventually i go through one or more use cases step by step in my mind or on paper.

sometimes i do that for days or weeks without writing any code (smoking weed and going for walks helps me to keep focused) and then i write the real code and if done well it works out of the box (after fixing some minor typo-like mistakes).

as a beginner i didn't plan ahead and just wrote code but that resulted in awful spaghetti code and circular dependencies everywhere and i ended up rewriting programs from scratch to fix the issues, only to run into new problems. looking how other engines are structured helps too.

  No.15069

File: 1458401930931.png (281.33 KB, 190x200, 1397512059699.png)

>>9922
>How do you do it?
I just start coding and deal with the fuck-ups and "maybe I shouldn't have done that" moments as they occur.

But I never make anything complex enough to warrant a plan anyway.

  No.15073

It's best to design your program in seperable "layers"
For example in a compiler the layers are really obvious
Read -> Tokenize -> Parse -> Desugar -> Type check -> Code gen -> Link

Each component depends only on the component before it. You should roughly design each one so that you won't realize half way through that you need to redesign the entire thing.

  No.16298

Bottom-up design is great. I program what I know I will need and join it as the whole when I get to that point.

I also try to keep my programs as small as possible. A single extremely complicated line is better than many simple lines.

I doubt many programs need to be larger than 1,000 lines of source code. For those that do, then I'd wager 10,000 lines would do for the vast majority of those.

It's like sculpting, in that you slowly shape your program as it's being created.

  No.16299

>>9922
>write, draw sketches, document desired behavior of the system in a very general way, try to understand how I want the "top-level" interface to work, e.g.:
>think of what output I want for particular inputs
>determine what information I feed into the system, and what information I want back or how it should react
>determine how I'll be interpreting that information as data the system can use and manipulate

>begin coding the highest-level part of the program that the user will interact with

>"wouldn't it be great if I had an X to do Y?"
>yes, give it a name and use it as if it already existed
>continue until top-level outline is finished
>now have a "wishlist" of procedures/etc. that need to be written in order to get it to work
>determine what information/data they need and how they manipulate it and interpret it
>write tests, procedure stubs that give dummy outputs of the correct type/form, just to ensure data is being handed between them correctly

>begin coding, following similar process as above

>when finished, ensure tests complete, examine edge cases, inline procedures that are better off being inlined instead of existing on their own, move procedures into the scope of others if they use their inputs, etc. and general clean-up
I find this is a pretty good way of getting things done. Acting as if the procedures/etc. you need already exist, and then writing them later reduces a lot of the initial sluggishness of a project, where you know you need something but don't know exactly what it is. Thinking of procedures in terms of data structures is very efficient too, and gets rid of a lot problems (less errors about passing wrong data or input, or misunderstandings about what that data really represents). If I need to refactor something big, the fact that the project is organized into "layers" around different structures of data makes that easy to do without breaking everything else. While it doesn't occupy a big part of the list above, most of my time is spent thinking about data and how to structure it. After that, the rest of it is pretty straightforward. All of the procedures (labeled by the data they input/output) are organized around data structures and then layered over one another. As long as each layer receives the appropriate structures and forms of data, they won't break. As long as the tests complete, it works. Since I make sure to examine the data first, it's easy to build tests to cover the edge cases.

Overall, I design top-down and then build bottom-up. I find that when I start a large project I already have an idea of how I want to behave "overall", but don't know the specifics. Designing top-down lets me figure out the specifics as I go along and have a scaffolding set-up by the time I'm ready to build. Then I know how to build the system up in terms of primitives and layers.

  No.16331

>>16298
> A single extremely complicated line is better than many simple lines.
I hope you're joking here.

  No.16332

>>16331
I use APL, so I'll hold on to the idea that a single complicated line is better than many simple lines.

What would you rather read: a single complicated line or ten simpler lines?

  No.16335

>>16332
Most people go for the latter. It's easier to understand the overall "architecture" of the overarching procedure when you break it up into small, well-defined parts well-named procedures. Compilers often/always inline the calls anyway, so there's rarely any overhead. IMO, where the performance (and length) differences are negligible, it's better to go for a smaller conceptual overhead than a smaller line-count.

Worrying about line-count seems like a red herring. I think one should be worrying about "conceptual blocks", like whether or not everything in that module or unit of code is related to each other and belong together conceptually, or whether they should begin to be separated out into different modules that each deal with their own concepts and structures so they can interoperate in other modules.

i.e., I write a function that traverses an array or list of data. If all I want to do is simply increment a list of numbers, I might as well inline it. If each item on the list is a more complex data structure, which has to be handled in a particular way with its own selectors and operations, and the way I have to think about that structure and its procedures in order to handle it is substantially different than the procedures used to handle the array/list containing it, then I'll just give that procedure a name, call it on each element, and then define it separately from it's parent procedure.

  No.16337

>>16335
>Most people go for the latter. It's easier to understand the overall "architecture" of the overarching procedure when you break it up into small, well-defined parts well-named procedures. Compilers often/always inline the calls anyway, so there's rarely any overhead. IMO, where the performance (and length) differences are negligible, it's better to go for a smaller conceptual overhead than a smaller line-count.
My advice is applicable.
Compressing functionality doesn't preclude functions and organization. It may just mean that the function only consumes one line instead of twelve.

>Worrying about line-count seems like a red herring. I think one should be worrying about "conceptual blocks", like whether or not everything in that module or unit of code is related to each other and belong together conceptually, or whether they should begin to be separated out into different modules that each deal with their own concepts and structures so they can interoperate in other modules.

The line count and "conceptual blocks" are typically closely related.
If you can fit an entire program on your screen, where previously it wouldn't, that helps the understanding.

>i.e., I write a function that traverses an array or list of data. If all I want to do is simply increment a list of numbers, I might as well inline it. If each item on the list is a more complex data structure, which has to be handled in a particular way with its own selectors and operations, and the way I have to think about that structure and its procedures in order to handle it is substantially different than the procedures used to handle the array/list containing it, then I'll just give that procedure a name, call it on each element, and then define it separately from it's parent procedure.

Each procedure defined can still be made compact.

Syntatic conciseness closely correlates with ease of understanding in a great deal of circumstances.

In the end, it's also very fun to make a program fit in as little space as is possible.

  No.16339

>>16337
I don't think the other poster was against concise, short functions. He asked if you were joking because you said that complicated lines were better than simple ones. In many languages, I'm sure you know, complicated inline functions tend to be longer and less concise than smaller ones. I think there was a misunderstanding there, so I'm not going to push it further.

  No.17343

Start with your inputs, map the processes to generate the desired output, and package them together in a single pipeline.

If you're doing some kind of continuous process without a termination condition other than a straight ctrl+c, ensure as few steps as possible between start and end of the loop. This ensures safe (ish) execution and limited points of failure.

For threaded programming, the less shared memory, the better. If you can, treat your threads as lambdas, and only allow them to modify the memory they're allowed to access. If you must pass data between threads, use flags, events, or if latency isn't a concern on the millisecond level, step up into a socket and leverage existing tools for async communication.


Never initialize, return, or operate with null values. If you have to define a "uninitialized" value, do so. Operations will remain valid regardless of the state, allowing programs to handle edge cases how you explicitly define them to respond, rather than whatever specific method the language chooses. This goes doubly so for languages with both null and undefined.

These are the four principles I've kept to when designing/writing applications. Whenever you feel you have violate a principle, reevaluate your design.

  No.17434

>>9945

t. torvaldski

pls to attribute quotes

  No.17814

I haven't found a particularly good way to get around this other than experience. Some people swear by 'design patterns', but I generally don't find them particularly helpful (that's not to say I don't use them, but it generally doesn't make the design process any easier if you're trying to shoehorn the system into a handful of design patterns).

  No.17815

>>14247
What useful project did you write in Scheme?

  No.20578

>>9922

My exact tactic tends to change with the problem I'm working with. When I deal with data-structures I write them out on a napkin to get a better visualization. I even sometimes go iteration by iteration with this model to make sure my data-types conform and behave like I expect/intend them to.

For more Object oriented languages I would write out my inheritance model along with what methods get passed around on a piece of paper and play with the bubbles until I got a good sense of the underpinning design.

In a language like Haskell, I tend to play type Tetris in my head along with the next technique.

The other major technique I always employ is just throwing basic functions at the REPL in order to confirm functions behave as I expect. In Haskell this would be help conforming my Type Tetris and for the quick check of function input and output, in Lisp this would be testing simple operations and testing finished functions (which end up allowing me to quickly morph and conform functions to better forms), and for languages like Java I end up just using it in order to try out the weird syntax they have introduced.