Ask HN: What change in your programming technique has been most transformative? - <AUTHOR> As an example, the team I work on has been adding more precondition checking to all of our applications. The simple act of stepping back from the perceived data-flow and explicitly declaring what we believe should hold true has uncovered several bugs in our understanding of our applications.
We've likened it to having someone review a paper you've written: you often read what you think you wrote, not what's actually written.
This got me to questioning what others have found to be transformative in their development practices. <comment><USER2> Programming in a team and accepting that it is not 'my' code but 'our' code. Not feeling slightly pissed when someone else changes the (not my) code. For me, this was a total game changer and a complete change in programming style. The focus changed to using the most common idioms, the clearest and cleanest and most concise way of writing stuff down, and avoiding smart hacks or, if necessary, encapsulating and documenting smart hacks. Not being shy of writing stuff multiple times until it really is clean and inviting to be read and understood. Using a very strict style (including when/where to put white space and line breaks) that everyone else also uses so that code is easy to read for everyone. It improved my programming enormously and the bugs/line ratio went down. It also gave a much better intuition to smell fishy code. It also took quite a while to internalise, but I now always code like that, also in private projects and in quick hacks scripts in, say, Perl.

My second most important change was to learn how to use contract based programming, or, if the language has poor support, at least to use a ton of asserts. This, for me, feels like stabilising the code very quickly and, again, improved my bug/line ratio. It forces me to encode what I expect and the code will relentlessly and instantly tell me when my assumptions are wrong so I can go back and fix that before continuing to base the next steps on broken assumptions.

<comment><USER3> I agree with the first bit; however it's hard to find a balance between accepting that your code can be improved, and standing by your solution. What I see in my team is that someone else has another solution, and whatever that is, should be implemented, if you argue against it, then it's often "I'm just trying to improve your code". However his solution might not necessary be better, it's just a different approach.

We also follow very strict style guides, but this is all defined in formatters and linters, so there's no arguing against it.

Even for my personal projects, the first thing I do is usually set up .editorconfig and TSLint (if I use Typescript).

</comment></comment><comment><USER4> I would say: Automated testing and a functional programming mindset.

Even for legacy projects, starting with adding regression tests for every bug you find is a great way to introduce testing. And when you add new features, you can write tests for that as well.

I find that a FP mindset is helpful mostly because it tends to reduce the amount of global or spread-around state. This in turn makes testing and quickly iterating in a REPL a lot easier. Also when later the time comes to debug, it's much simpler if you don't have a huge amount of state to set up first.

And even if a lot of state is required, having it be an explicit input to a procedure is helpful because it makes it much clearer what you need to set up when doing manual testing.

<comment><USER5> You had me at automated testing.

<comment><USER6> +1

</comment></comment></comment><comment><USER7> It's okay to be messy.

Treat code mess with the same techniques you would treat RL mess. Sometimes you sweep it under the rug. You can toss it in a closet or attic. You can buy a shelf or box and toss everything in there.

You can clean it up seasonally, like set aside a sprint for it.

Some kinds of messes are hazardous and absolutely should not be tolerated - this is similar to leaving milk out or trash piling. Many people make this mistake of assuming that because some messes are very dangerous, all of it is too.

Most messes will slow you down, but cost more to clean up. Overall, I've seen documentation do more harm than help - it's often faster to interrupt a colleague doing something than for that colleague to spend weeks documenting and updating documentation on something that gets thrown away.

Some will take more time to fix later than now - this is what people mean by tech debt. But it's less common than it seems.

With a lot of mess piles, the important parts float to the top and the less important ones sink at the bottom. God classes are mess piles.

Once the mess becomes a burden, it's a good time to clean up.

You may need a few janitors and landscapers, especially for a large code base.

Some people place much higher priority on cleanliness than others. Be respectful of them but you don't have to be like them.

<comment><USER8> Wrong. Absolutely wrong analogy.

You can't discount people who keep code clean, modular and simple. It's not an easy thing to do unlike cleaning your house. It just takes one's will to clean the house but when it comes to keeping your codebase clean, it's much more than just will.

Sound knowledge of software architecture and design principles is required to write and keep your code clean and unfortunately, the number of people with this knowledge is very small in most teams and sometimes there's none.

Also, it's not okay to be messy unless you are working on a school/university project.

<comment><USER9> > It's not an easy thing to do unlike cleaning your house. It just takes one's > will to clean the house but when it comes to keeping your codebase clean, > it's much more than just will.

Let me put it this way. I've seen no relationship with code purity and a happy client

<comment><USER10> Have you ever walked into a restaurant and asked to see how they refrigerate or organize their ingredients?

No? Would you say then that you don't care if your food comes out slowly or gives you food poisoning?

You care about the result, but you trust it to someone else.

It is a Chef's responsibility to maintain the mise-en-place and food safety to a degree which enables him or her to keep delivering meals quickly and without salmonella.

It is an Engineers's responsibility to maintain the code clarity and tests to a degree which enables him or her to keep delivering improvements on business needs quickly and securely.

<comment><USER9> That analogy would work if I hadn't worked for companies who made $400m+ with code and processes that would be at home in a 14 year olds hobby project. Oh and 0 tests.

I find a lot of a certain kind of sw person who need analogies to make their mental model work. It never did for me

</comment></comment></comment></comment></comment><comment><USER11> Learning different programming paradigms. For example, logic programming with Prolog makes you think about solving certain problems quickly and efficiently in a declarative style. Strongly-typed functional languages like SML and OCaml make it easy to use types and pattern matching to reduce errors and shift some cognitive burden from yourself to the compiler. Lisps allow you to quickly prototype functions in the REPL and test them interactively, and thinking about code as data (homoiconicity) is a powerful concept.

In short, learning new programming paradigms completely changed my view of programming and these skills can translate over to more "mainstream" languages, so it is still a worthwhile effort.

<comment><USER12> >shift some cognitive burden from yourself to the compiler

This is the best way I've seen the advantages of static type checking articulated.

<comment><USER13> The line I use is: "I'm a very bad programmer, but I'm very good at making the compiler force me to get it right".

</comment></comment></comment><comment><USER14> You can come up w/ an algebra for a specific problem domain by thinking about the data models and repurposing some simple abstract algebra.

E.g.:

        new(state, &args) => mutation
        apply(state, mutation) => state'

Applied to finance:

        newPayment(balanceSheet, amount, date) => payment
        apply(balanceSheet, payment) => balanceSheet'
        newDiscount(balanceSheet', amount, date) => discount
        apply(balanceSheet', discount) => balanceSheet''

Applied to chess:

        newMovement(board, K, E2) => movement
        apply(board, movement) => board'
        newMovement(board', b, D6) => movement'
        apply(board', movement') => board''

As a bonus, it's clear where to enforce invariants and fail early:

        newMovement(board', K, E3) => Illegal Movement (Can't move to E3)
        newMovement(board'', K, E2) => Illegal Movement (White King not available on board)
        apply(board''', randomMovement) => Illegal Play

<comment><USER15> You'd like: [https://blog.ploeh.dk/2017/10/04/from-design-patterns-to- cat...](https://blog.ploeh.dk/2017/10/04/from-design-patterns-to-category- theory/)

</comment></comment><comment> aaron-santos Hot code reloading. It was one of the best decisions I made when I added it to a hobby project I've been working for the last six years. Most of my development time for this project is spent adjusting code while the app is running. The feedback loop is incredibly tight and the most engaging of all of the projects I've ever worked on.

I'm working on extending auto reloading to all of the assets in the project because I know that tight feedback loops are that important.

<comment><USER16> What language? Is it a game?

I've been wanting to try that ever since I saw a video of someone developing an FPS in Common Lisp while playing it at the same time. They would modify the bullets and the way they made collision, then fire after each change to see the effect.

<comment><USER17> Sounds like you're remembering John Carmack developing VR using racket:

[https://www.youtube.com/watch?v=ydyztGZnbNs](https://www.youtube.com/watch?v=ydyztGZnbNs)

edit:

Also check Arcadia for Clojure:

[https://www.youtube.com/watch?v=_p0co13WYPI&feature=emb_titl...](https://www.youtube.com/watch?v=_p0co13WYPI&feature=emb_title)

And developing flappy bird in clojurescript (canonical figwheel example):

[https://www.youtube.com/watch?v=KZjFVdU8VLI](https://www.youtube.com/watch?v=KZjFVdU8VLI)

<comment><USER16> No, the one I saw was different. They were walking around the world and shooting stone building walls. They also weren't part of a conference. The video was just their screen split with their code on one side and the game window on the other. I'm pretty positive it was Common Lisp too because I think I was looking for SLIME videos at the time.

Thank you for the links.

</comment></comment></comment></comment><comment><USER18> 1\. Using REPLs: The tool or the principle.

REPL means Read-Eval-Print-Loop and its a place where you can run code immediately and get immediate feedback. For example F12 in your browser and use the console to do 1+1. But you can also use that console to interact with your code in the browser, the DOM and make http requests.

But I also see REPL as a principle - to get as quick feedback from the code you write as possible, and things that help this are:

* Quick compile times

* Unit tests

* Continuous integration

So that each stage is as quick as possible. I write code and within as second or two know if it failed a test. Within a few seconds maybe I can play with the product locally to test it manually. Once committed, I can see it in production or a staging environment at least pretty quickly.

You can then have bigger REPL loops where a product manager can see your initial code fairly quickly, give you feedback and you can get stated on that right away and get it out again for review quickly.

I don't think there is any excuse not to work like this given the explosion of tooling in the last 20 years to help.

2\. YAGNI

Writing over elaborate code because it is fun! That's fun at first but you soon learn it's better to write what is needed now. There is a balance and writing absolutely shit code is not an excuse, but adding too generic code because of stuff that might happen is also a problem.

</comment><comment><USER19> By far, starting to write automated testing as I code. The sheer number of bugs I find immediately and the number of bugs I find from changes that I didn't expect would be enough to justify it, but what is even _better_ is that it forces me to keep my code _testable_ , which also happens to correspond fairly closely to _well-architected code_. Not perfectly, but for something that is automated and always running, it's really helpful.

When I'm able to greenfield something myself, and use this from day one, I tend to very naturally end up with the "well-separated monolith" that some people are starting to talk about. I have on multiple occasions had the experience where I realize I need to pull some fairly significant chunk of my project out so it can run on its own server for some reason, and it's been a less-than-one-day project each time on these projects. It's not because I'm uniquely awesome, it's because keeping everything testable means it has to be code where I've already clearly broken out what it needs to function, and how to have it function in isolation, so when it actually has to function in real isolation, it's very clear what needs to be done and how.

Of all the changes I've made to my coding practice over my 20+ years, I think that's the biggest one. It crosses languages. At times I've written my own unit test framework when I'm in some obscure place that doesn't already have one. It crosses environments. It crosses frontend, backend, command line, batch, and real-time pipeline processing. You need to practice writing testable code, and the best way to do it is to just start doing it on anything you greenfield.

My standard "start a new greenfield project" plan now is "create git repo, set up a 'hello world' executable, install test suite and add pre-commit hook to ensure it passes". Usually I add what static analysis may be available, too, and also put it into the pre-commit hook right away. If you do it as you go along, it's cheap, and more than pays for itself. If you try to retrofit it later.... yeowch.

<comment><USER5> Ah man. Reading these comments make me so happy. Automated testing from the start. Code as you go. Love every bit of it.

<comment><USER20> To piggy back on here. Don't use mocking frameworks in tests. I've found it to be a great way to expose design flaws in the code.

</comment></comment></comment><comment><USER21> Avoiding cleverness at all costs. Simple code that is easy to both read and write without strong efforts to follow senseless DYI or take on abstraction for the sake of abstraction.

<comment><USER22> Can anyone give a specific example of code they've worked with that was "too clever"?

Based on my experience, if a coworker wanted to "avoid cleverness" I imagine we'd end up arguing over something like a for-loop versus a map, but such small code decision matter very very little in comparison to overall architecture, and where you place the "seams" in your system.

So I ask, when you have felt that code is "too clever", was it because someone used a map, but you're more comfortable with for loops, or was it bigger than that?

My first job was maintaining some PHP. The author didn't know what a function was. The code was a 5,000 line script, top to bottom, with basic control and looping logic nested up to 17 deep (I counted). It was horrible. Yet surely, the author had avoided cleverness at all costs; he had built a working system with only the most basic tools, those being all he knew.

<comment><USER23> Clever:

        for(let i=0;i<100;)console.log((++i%3?'':'fizz')+(i%5?'':'buzz')||i)

Readable:

      for (let i = 1; i <= 100; i++) {
          if (i % 3 == 0 && i % 5 == 0) {
            console.log("FizzBuzz");
        } else if (i % 3 == 0) {
            console.log("Fizz");
        } else if (i % 5 == 0) {
            console.log("Buzz");
        } else {
            console.log(i);
        }
    }

<comment><USER24> I like that example. Since I'm preaching to prefer functional style programing and to separate the side effect from the rest of the algorithm, I'd write the code this way:

      Array
      .from({length: 100}, (v, k) => k+1)
      .map(i => {
        if (i % 3 == 0 && i % 5 == 0) return "FizzBuzz"
        if (i % 3 == 0) return "Fizz"
        if (i % 5 == 0) return "Buzz"
        return i
      })
      .forEach(console.log)

</comment></comment></comment></comment><comment><USER25> Functional Programming fundamentally changed the way that I solve problems. It also changed my expectations for how code should be designed.

The concepts that influenced me the most were immutability and the power of mapping+filtering. Whenever I read a for/while loop now, I'll attempt to convert it to the FP equivalent in my head so that I can understand it better.

<comment><USER26> map and filter are not concepts of FP - they are just syntactic sugar around for loops. fully evident if you check any native source code. many languages (such as go which I currently use) do not even have these functions but can apply FP. this is by design of simplicity and visibility: it does not get much easier to read than a simple for loop and know the exact iterations count at runtime.

<comment><USER27> One of the key principles of functional programming is, well, functions. A for loop isn't a function and isn't composable, map and filter are.

</comment></comment></comment><comment><USER28> Recognizing that there's a special power in resilient technologies. Those that keep chugging along decade after decade, and getting stronger, too. Not from inertia or monopolist effects or other kinds of random lock-in. But because they tackle a certain set of fundamental problems very, very well. Despite endless complaints about their fundamental limitations, conceptual flaws or alleged lack of scalability.

SQL, Python, PHP, JavaScript, and many aspects of Unix and the C/Make toolchain all come to mind.

Mind you, I don't _like_ all of the above. At least 2 in particular I definitely wouldn't mind seeing "just go away."

But I do recognize that they have special _staying power_. And they didn't get this power by accident.

<comment><USER29> This is certainly not true. Evolution of programming languages is _circumstantial_. There are many flaws in all of the ones you had listed.

The marginal cost of deprecating something or breaking something is higher than the incremental cost of workarounds. This happens all the time from the laryngeal nerve in Giraffe to the evolution of a programming language, say JavaScript.

Please don't mix good design with popularity. It's a correlation/causation fallacy.

<comment><USER30> Look close enough and everything is flawed.

Evolution doesn't 'design' anything, least of all anatomy.

The 'special power' is that they have stuck around, for whatever reason or circumstance.

<comment><USER29> I never claimed that evolution "designs" something. In fact, I'm talking about the opposite - Evolution has no hindsight. Therefore, the marginal cost of undoing something is larger than incrementally adding or bolting on fixes.

</comment></comment></comment></comment><comment><USER31> Making debuggability (transparency) a first-class design criteria.

For many (most) systems, there are designs that make perfect sense, but will be hard to debug.

When I started designing so that programs could tell me what they are going to do, what they are doing, and why, and so that their state, wherever possible, could be expressed into a structure where I could "see" the wrongness, my development time went way down--because it was really time spent debugging, so my productivity went way up.

<comment><USER32> Interesting. Any examples of libraries or papers?

<comment><USER31> I can give an anecdote. I needed to design a load testing system that would ramp up to an arbitrary number of users of certain types, over an arbitrary amount of time.

Rather than just make a loop and increment every so often, I made the system output it's "plan" for the user ramp, and a second component execute the ramp plan. We had a bug in the calculation which we were able to see easily by reading the "plan." Without this step, we would have been unsure if it was the calculation of when to add users, or the implementation of adding a user that had a problem.

</comment></comment></comment><comment><USER33> For me it was three things: 1) learning to understand push/pull between data structures and algorithms 2) learning new programming languages from different paradigms, and 3) understanding the physical implications of programming (i.e. latencies of disk-access, network, and memory; the non-amortized cost of pulling data from a database only to do the joins in memory, etc.)

My younger self did not truly understand the strength of modeling or treating data as the reification of a process. I saw everything through the lens of what I knew as a programmer which was, for the early 2000s java developer I was, a collection of accessors, mutators and some memorized pablum about object orientation. I treated the database with contempt (big mistake) and sought to solve all problems through libraries.

Now I can see the relational theory in unix pipes. I can see the call-backs of AWK and XSLT processing a stream of lines/tuples or tree nodes as the player- piano to the punch cards. I understand that applications come and go, but data is forever. I no longer sweat the small stuff and finally feel less the imposter.

</comment><comment><USER34> I throw away rough drafts all the time for non-trivial work that I don't fully understand yet. There are different approaches to it, but I usually just create a throwaway branch to hack out a naive solution until I run into the non-obvious roadblocks and then try again. I used to try to _really_ understand something before coding a solution, but not being afraid of throwing away a rough draft has helped my mindset a bit in terms of working out difficult problems. It's not a novel or huge concept but it works for me.

<comment><USER35> This is an interesting idea, I think I often do this subconciously but I might take your mindset next time and tell myself I can chuck the first draft away.

</comment></comment><comment><USER36> Static typings + type inference

Parse, don't validate [https://lexi-lambda.github.io/blog/2019/11/05/parse- don-t-va...](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t- validate/)

All libs/products should be pure functions, with input output documented, making libs/products predictable

Use in-app event sourcing to reduce the need for global states states

DoD [https://youtu.be/yy8jQgmhbAU](https://youtu.be/yy8jQgmhbAU) In non bare- metal languages, this will be useful for readability

For errors, return instead of throw

<comment><USER37> Thanks for that "Parse, don't validate" link. It definitely is the clearest explanation for type driven development I've found.

Turns out, years ago I was writing some C# code and I found myself wanting to explore something like that. However, it wasn't idiomatic for C# (especially at the time), and I talked myself out of it.

Since then, I've wondered if that's what people mean by type driven development. It's cool to see that is at least in the general ballpark.

<comment><USER36> Is C# nowadays friendly enough for type driven development?

Using TypeScript daily, I have a great time with type driven development. (C# and TS was created by the same guy)

I was never comfortable with Java community approach of automating stuff, with their heavy use of reflections, method name parsing instead of being strict in typings, use generics, macro, templating, etc.

<comment><USER37> Well, you can "force" some of it ... make an object whose ctor can only succeed if a certain check passes, for example.

You'd be using types in a way that pretty much no one in C# does (not idiomatic), except for maybe those who are explicitly trying to go for type driven development (for example, people doing things like railway oriented programming).

</comment></comment></comment></comment><comment><USER38> "Premature optimization" was happening a lot more in my head than on the screen. By that I mean I would waste time struggling to succinctly generalize a function instead of making something work the easy and ugly way FIRST. It is far easier to copy paste working code in 4 places and leave a nice comment on each about what could be probably consolidated... without actually doing the work at least until you come back to the code to make a minor change in those 4 places. Half the time you never come back, most other times you end up merging them within a week. And for outliers... good commenting saves the day.

</comment><comment><USER39> Some game changers for me off the top of my head:

* Automated regression testing and build/deploy pipeline. The machine will do things quickly and repeatedly exactly the same way, given the same input conditions/data.

* TDD. Create tests based on requirements, get one thing working at a time, and refactor with a safety net.

* Mixing FP style and OO style programming to get the best of both worlds.

* Understanding type systems, and how to use types to catch/prevent errors and create meaningful abstractions.

* Good code organization, both in physical on-disk structure and in terms of coupling & cohesion.

* Validate all incoming data and fail fast with good error messaging.

</comment><comment><USER40> Many developers think of themselves as abstraction masters. "Moar abstraction is moar better!" Abstractions have a cost in readability and maintainability. It is very hard to make the abstraction that improves both. I'm tempted to say that pre-abstraction is a larger problem than pre-optimization.

Make your code easy to read at the expense of easy to write. Don't abstract unless you know exactly the use case. If the use case is not immediately forthcoming, YAGNI (you aint gonna need it). Related, don't be clever. Clever is cute and all. It does not belong in production code.

</comment><comment><USER40> When working on most things, I ask two basic questions: how does it fail? how does it scale? I picked that up years and years ago.

Any line, function, workflow, etc can fail. You need to have worked through all the failure cases and know how you are going to handle them.

Somethings don't need to scale, ever. But most things I work on do. If I don't know the story on how I can ramp up an implementation a few orders of magnitude, then I can't say I've designed it well.

Aside from that, measure everything. Metrics, telemetry, structured logging. Design for failure and design for understanding what happened to cause the failure. Data will tell you what you are doing, not what you think you are doing.

There was a post here yesterday about a neat map thing. I hit a bug and the author could only rely on their experience with it being potentially slow at times but saying it should work. If there were proper metrics in place, they would know that N% of calls exceed M seconds and cause a timeout. He could then relate this to the underlying API and its performance as the service experiences it. With proper logging, they see what the cache hit ratio is and determine if new cache entries should be added.

Build, measure, learn.

Oh, and automated tests. So much to be written on that.

</comment><comment><USER41> Leaning on the type system for as many guarantees as possible. A few of my teams use Kotlin's sealed classes and extension methods to prevent a lot of goofy invalid state and improve composability (using a Result<T, E> sum type very similar to what Rust has built-in).

I liked this article I recently came across discussing the topic: [https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t- va...](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/)

</comment><comment><USER42> Continuous Delivery.

I'm building internally used software, and getting feedback from stakeholders in the testing environment was hard work, and often impossible.

We went from roughly monthly releases to deploy-when-it's-ready, and this tightened our feedback loops immensely.

Also, when something breaks due to a deployment, it's usually just one or two things that could be responsible, instead of 20+ that went into one monthly release. Waaaay easier to debug with small increments.

</comment><comment><USER43> Optimising for quick feedback loops.

Basically it involved writing additional code to set up the app state to the state I need.

It went from changing a line of code, running the app, clicking/filling fields as needed, and then seeing the change reflected, to the app immediately being in the state that I wanted.

Now that I'm working in web-apps, hot-reloading is quite beautiful.

</comment><comment><USER44> Many incremental changes (some already mentioned here) but none comes close to grokking macros in Lisp and lisp-like programming languages such as Common Lisp, Scheme, Racket, Emacs Lisp and Clojure. That was truly, truly transformative.

</comment><comment><USER45> Probably learning to use a debugger and decreasing my reliance on print() statements. It's a nonzero initial investment but saves time in the long run.

At the team level, probably CI/CD. It forces us to break the monolith into digestible chunks and makes regression testing easier.

</comment><comment><USER46> I've recently joined a team that focuses massively on test-driven development, and it's just such a great way to shake out dumb bugs and focus on the requirements and the problem you're trying to solve.

Also recently did Thorsten Ball's Interpreter/Compiler books which focus heavily on unit testing the functionality (I can't recommend these books enough).

I now can't imagine going back to a pre-TDD world

<comment><USER47> I did Learn Go With Tests which was my first intro to TDD and I found it enjoyable even if the author is little overly pedantic about his preferred testing approach.

However at work, I often find that I feel like I can't write unit tests before starting code because I just don't know how it's going to get built until I start poking at the code. I'm not sure how to break out of this

<comment><USER48> > However at work, I often find that I feel like I can't write unit tests > before starting code because I just don't know how it's going to get built > until I start poking at the code. I'm not sure how to break out of this

You'd start with shallow functions and quality asserts. Initially, you'd have broken tests, you have to write code to fix them, and that's your TDD.

<comment><USER46> This is definitely the best way. Start with the how you want everything to look and make your code match your expectation.

<comment><USER49> Hello, I wrote learn go with tests!

Your comment is exactly what I try to get across in the book but it's not always easy.

If you cant decide how you want something to look (as that's not always easy) just take a punt on something. Make sure it's a small decision and make something useful. Sure you might have to change it, but at least you'll be basing that on some real feedback.

<comment><USER47> Hi, thank you for writing the book! Great introduction to Go.

One thing I was uncomfortable with about TDD is the step to "just write enough to make the test pass". The danger seems to be when you have to walk away from some code and you or whoever picks up your code doesn't know what you were up to.

In a simple app and a few tests you could probably tell, but the larger an application gets, the less you can put effort into finding out "this test works because the application works how it should" or "this test works because someone wrote just enough code and hardcoded some value somewhere in order to make the test pass".

It seems "safer" to write tests that are going to stay broken until every little corner of everything works properly, even though this is less iterative.

As mentioned above I have little TDD experience, so maybe I'm missing something.

<comment><USER49> The point of "just writing enough to make the test pass" is to get you to the point where you have working software (proven by the test, even if the code is "bad")

This is the _only_ point where you can safely refactor, if your tests are failing how do you know you haven't broken something? So long as you keep things "green" you know you're ok

> The danger seems to be when you have to walk away from some code and you or > whoever picks up your code doesn't know what you were up to.

Just don't do this. You're not "done" with the TDD cycle until you make it pass and have refactored.

I reflect this in my git usage too, roughly it goes

\- Write a test -> Make it pass -> git commit -am "made it do something" -> refactor -> run tests (if i get in a mess, revert back to safety and try again) -> git add . -> git commit --amend --no-edit

> It seems "safer" to write tests that are going to stay broken until every > little corner of everything works properly, even though this is less > iterative.

The problem with this is how do you safely refactor when you have potentially dozens of tests failing?

A big point of this approach is it makes refactoring a continuous process and makes it easier because you have tests proving you haven't accidentally changed behaviour.

<comment><USER46> I need to get this book now too ;)

<comment><USER49> Well it's open source so just go take a look

[https://github.com/quii/learn-go-with-tests](https://github.com/quii/learn- go-with-tests)

</comment></comment></comment></comment></comment></comment></comment></comment></comment><comment><USER50> Writing simple functions which take an A and return B in any context. Then, composing those simple functions together to build up more complex functionality.

<comment><USER51> I would mention generics also. It has forced me to a clean architecture from the start without throwing away code.

Although sometimes the domain is not clear enough before you start, that's shitty.

</comment></comment><comment><USER52> Realizing that I can roll my own framework.

Why? Because I've tried to actually think about my underlying data structure instead of defaulting to convention but reflex.

The best example? Marcel Weiher Ch 13 in "iOS and MacOS Performance Tuning" explains how to improve lookups in a public transport app by 1000x.

What more? The description of the the data (the code) and the the application (the intended use) are probably way better because thinking about performance is similar to thinking about your data. You want answers fast. Fast answers, fast code.

</comment><comment><USER53> I've been doing a lot of Python lately and using type annotations strictly and a decent IDE like VS Code or PyCharm. If you screw up, passing a foo instead of a bar, both almost immediately highlight the error. Don't even have to run the script to see it blow up. Huge time saver.

Also doesn't hurt that it improves Intellisense/Autocomplete, gives better hints/help as you're calling a method, and improves on "self documenting" code.

</comment><comment><USER54> I ask myself what's the smallest, dumbest, least-coupled program I could possibly write to get the job done. Turns out that the answer is often "very", and short, simple programs are a lot easier to produce bug-free and work with over time.

</comment><comment><USER55> I wouldn't be surprised if the majority here are doing this, but version control everything

Distributed version control is one of the greatest things to have gained popularity in the last ~20 years; if you're not incorporating git, hg, fossil, ... start now.

You gain: concise annotated commits, ease of cloning/transporting, full history, easy branching for feature branches or other logically seperate work, tools like bisecting, blame/annotation, etc., etc.

</comment><comment><USER56> I can think of 4 things:

1\. Not being afraid to look at the code of the libraries that my main project depends on. It's a slow, deliberate process to develop this habit and skill. But more importantly, as you keep doing this, you will develop your own tactics of understanding a library's code and design, in a short amount of time.

2\. Not worrying about deadlines all the time. Not a programming technique as such, but in a world of standups and agile, sometimes, you tend to work for the standup status. Avoiding that has been a big win.

3\. (Something new I've been trying) Practicing fundamentals. I know the popular opinion is to find a project that you can learn a lot from, but that may not always happen. Good athletes work on their fundamentals all the time - Steph Curry shoots like > 100 3 point shots everyday. I'm trying to use that as an inspiration to find some time every week to work on fundamentals.

4\. Writing: essays, notes. In general, I've noticed I gain more clarity and confidence when I spend some time writing about a subject. Over time, I've noticed, I've become more efficient in the process.

<comment><USER57> What would be some fundamentals to practice for a software developer?

<comment><USER56> There are quite a few, and you can create a list of categories you consider as fundamentals for the nature of your work. As an example, I would think Algorithms and Data Structures is a fundamental subject. These are the easiest to practice.

You could for example pick something as simple as a HashTable, and implement it from scratch. Then, you could add more complexity to it, like HTs that won't fit in memory, expanding and shrinking HTs efficiently etc.

Or, you could use one of the several practice websites like LeetCode to practice Algorithms/DS problems.

Once you start building a habit, you will also become better at organizing your practice routine and finding out more about what to work on, and where to look for study/practice materials. But mind you, this is a slow process, which you want to build as a habit. There is no end goal here (like cracking Google interview or such), this is a process to get better at the fundamental skills in your field.

</comment></comment></comment><comment><USER58> Moving from my college years of emacs on the server to a proper IDE on the desktop.

<comment><USER59> Do you use any emacs key bindings in that IDE?

<comment><USER58> Yes, but compared to an IDE (Pycharm in this case) there is no comparison.

</comment></comment></comment><comment><USER60> Honestly at a team level - code reviews. At least one other developer reviewing every line of code that is written is transformative in the quality of code that is produced. A lot fewer problems, a lot more maintainable.

</comment><comment><USER61> Autoformatting (gofmt, prettier, etc). I can't believe how much time I was wasting having to manually indent, split lines etc.

<comment><USER47> +1... black for Python. I didn't agree with some of the formatting decisions, but it has gotten better with passing versions, and the gains of not thinking about it at all are huge.

</comment></comment><comment><USER62> Self discipline. Just sitting down and showing up is not enough, you need to _concentrate_. This is why I think open plan offices are terrible for getting real work done, there always should be possibilities for solitude for those that need to work on something hard. Oh, and KISS. No way without that.

</comment><comment><USER63> Can't believe this list doesn't include revision control systems. So many benefits. Without these, you can't easily work with others. Never again break code then scratch your head wondering how to return it to previously working state (we've all been there). Have a documented history of changes over time. Support for branching. Also amazing for efficiency and not so frequently used are pre and post commit hooks for CI/CD.

Basic inline documentation. Who wrote it, when and why. What else did you consider. How does it differ from other solutions. Why is it designed the way it is. Brief history of design changes. What state did you reach in testing. What are the forward looking goals. Takes 5 minutes, pays for itself many times over in future. 90% of effort is maintenance.

</comment><comment><USER64> Immutability. I don't use it all of the time. And sometimes I use bits of the knowledge I've picked up through playing with it. But encountering it made me think a lot more about how state is available through the systems I write, and that definitely made me a better coder.

</comment><comment><USER65> Logger as an anonymous function.

I think literally every logger let's you log at a certain point as a single line that does not cover the context of the log.

      logger.info('Doing this and that',  function () {
        do_this()
        if(maybe) do_that()
      })

This way you know how much the log comment is supposed to cover as well as take a benchmark on the said context.

I can never think to just add a single line logger anymore.

This is much better than inserting comments in a code as comments can have no context except the line below.

You can add a function like,

logger.comment

and don't let it log anything to be comment syntax replacement.

<comment><USER66> Not an active HN user. There doesn't seem to be a downvote function..

<comment><USER65> I guess you can't downvote having below 500 points for yourself.

You can certainly add a comment on how you don't like it.

</comment></comment></comment><comment><USER67> State Machines.

Thankfully that was decades ago, which means I enjoyed the magic and bliss for most of my professional career.

Also interesting: I started using state machines in hardware designs before I applied them in software.

</comment><comment><USER68> Environment variables with fallback to a default value.

They're a super cheap way of

1\. allowing feature flags

2\. injecting credentials in a way the user thinks about exactly once

3\. moving workstation-specific details out of your code repository

They're implemented into the core of most every language in existence (especially shell scripts) and you're probably already using them without knowing. They're (get this) _variables_ for tuning to your _environment_.

Sounds like I'm being sarcastic here (eh, maybe a bit) but it never really hit me until I really dug into the concept.

</comment><comment><USER69> Honestly, I used to be such a computer nerd, to a fault. So over the last quarter century of programming for money, I'd have to say the most transformative has been writing comments like my livelihood depends on it (since it does).

Other development practices that boosted my output significantly: Regular cardio exercise like running, strength training by lifting weights, and regularly reading source code for pleasure.

</comment><comment><USER70> Using [https://github.com/watchexec/watchexec](https://github.com/watchexec/watchexec) to evaluate code as I save.

[https://github.com/nikitavoloboev/dotfiles/blob/master/zsh/a...](https://github.com/nikitavoloboev/dotfiles/blob/master/zsh/alias.zsh#L15)

<comment><USER63> Re: watchexec, another two patterns are: (1) trigger processes using pre- commit hooks with your local RCS/VCS. (2) replicate core watchexec functionality using inotify-tools, fsnotify or similar. As you seem to be using go also consider [https://github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify)

</comment></comment><comment><USER71> Async-await in Rust has made asynchronous programming a joy. It's no wonder that programmers are refactoring as fast as they can to support it.

</comment><comment><USER72> Not a programming technique, but a mindset. If you separate your ego from your code ("My code doesn't define my value") then it becomes much easier to look at your code objectively and understand others' criticism. This allows you to grow faster and more openly (in directions you may have opposed before).

</comment><comment><USER73> Writing a basic test before starting a task, and use that to verify that the task is correct. I use the word task because this can apply outside of writing code, for example making some large scale changes to data. Never underestimate your ability to overestimate your ability. Reality is hard so make the machine help you.

</comment><comment><USER74> Something not mentioned yet. I find programming is the task of last resort and I spend a good deal of my time talking with the product person to help them refine their product idea into something that is mechanically sympathetic with how computers work and the general problem domain. It often adds 10% time for the initial work and -90% of the time for iterations.

For example once we had a project that wanted to add Role Based Access Control and some juniorish engnineer suggested adding boolean columns to the table for each of the user's roles.. Nope. Instead we created a document store w/ roles and defining new roles was as simple as adding a const to a set in the service's code. Good thing too because the number of roles grew from the initially requested ~3 to almost 100. That would have been too wide of a table.

<comment><USER51> I would have used flags if roles aren't dynamic

<comment><USER74> in our case they were not only dynamic, but also managed by the administrator on an account (by our customers, not by us the software creator)

<comment><USER51> Not that hard, if there's something like a Tenant implementation.

Which would limit the scope of the "moderator" role explicitly to the tenant database.

Eg. [https://martendb.io/documentation/documents/tenancy/](https://martendb.io/documentation/documents/tenancy/)

Fyi: The op, which i was responding too, was referring to using roles as const. Which is not dynamic :)

</comment></comment></comment></comment><comment><USER75> 1\. Write notes, when notes can't support ur thoughts draw, scribble. Date every page. Revisit the notes every 2-3 days. Put priority on the idea. Write notes when you about to get off work or before you go into a boring or interesting meeting and chances are you might forget what you were thinking.

2\. Don't get into the trap of refactoring just because you read a new cool way of doing it unless, it's reduces half the size of code or improve performance multi fold. This might waste time as you could write a new feature or plan about it in that time.

3\. Write very big elaborative comments. It's for future you as you can't remember everything, sometimes you need to know why you wrote that code or condition as you might not remember why you wrote it at that time.

</comment><comment><USER51> If you have to run your code to understand it.

Try again.

<comment><USER76> This is so true for beginning developers. They tend to have a mindset that once they run something and it appears to work, then that means they likely got it right.

In reality the code might be right for 90% of use cases, but once you multiply this over a whole project, you end up with total dogsh*t, and mainly because of laziness too. This is the biggest reason the inexperienced developers create such horrendously buggy code.

</comment></comment><comment><USER77> Migrating from JavaScript to Typescript and declaring interfaces for classes and data structures forced me to put more thought into each piece before I start implementing. Now I've cleaned up a lot of old code I didn't have much visibility over thanks to the compiler warnings.

</comment><comment><USER51> Using static typing to make changes slightly bigger than intended.

But with a better architecture and nearly zero mistakes. Through using OO correctly ( please read the word correctly)

One step at a time.

Ps. This is mostly when a code-base is detected where a part of the code is sensitive for bugs. Eg. From today: handling better payment providers and payments.

I thought it was important enough to make the change bigger, but with the intention of removing recurring/similar errors in someone else his code-base.

Afterwards, go to him and explain why. Developers can be proud of their code, although it's just shitty code ( eg. Long functions, loops, if else, switch, by reference, ... Is mostly an example of bad code) and never say it's shitty..

</comment><comment><USER78> Off the top of my head:

\- Reading books (e.g. Clean Code, Refactoring: Ruby Edition, Practical Object-Oriented Design, ... these left a mark)

\- Going to conferences (Rubyist? Attend a talk by Sandi Metz if you have a chance)

\- Testing (TDD at least Unit Testing)

\- Yeah, the usual stuff: quick deployments, pull requests, CI/CD ...

\- Preferring Composition over Inheritance (in general and except exceptions :)

\- Keeping on asking myself: what is the one responsibility of this class/object/component?

\- Spending a bit more time on naming things

\- Have a side-project! It's a fun way of learning new things (my current one is [https://www.ahoymaps.com/](https://www.ahoymaps.com/) \- I could reuse many things that I learnt also in my 9-5)

</comment><comment><USER79> FP, REPL Driven Development and generative testing.

In short, I have been doing Clojure since last year.

</comment><comment><USER80> I stopped doing small commits. In the past as soon as I was "done" with something I would commit, even if one line.

What would end up happening sometimes later in the day I would decide revert or modify the change, so my commit history ends up flooded with bunch of small commits.

I ended up writing a script that checks my current work, and if enough changes or time has past, then a commit is recommended:

[https://github.com/stvpn/sunday/blob/master/git-train/git- bo...](https://github.com/stvpn/sunday/blob/master/git-train/git-board.php)

<comment><USER54> You can have your cake and eat it too by initially generating a series of small commits and then going back later to reorder them and squash them into a much shorter series of logically coherent commits.

This makes it way easier for someone (possibly you) looking later to understand what was done and why.

(I once almost took a job where the repo was just a series of huge commits taken at more or less regular intervals, but with no logical coherence and no attempt to document what was happening. Noped right out of there.)

<comment><USER81> This is exactly what I do, with one addition: Sometimes I don't check everything in as I go because I'm doing a lot of experiements at high speed. However, I will locally do "junk" commits for trivia such as comment typos and short, obvious bugs that are noticed.

At this stage I don't worry much about the commit message, as it's purely local, and many of them will be squashed (discarding the message). So the trivial ones get one-liner messages like "WIP: Adder: Comment fix" or "WIP: Parser fix '...', needs testcase'. "WIP" commits are a useful tag for me: They are never allowed to be pushed to a shared repo.

Then the "git add -p" stage.

When a task, feature or bug has been dealt with, I'll start using "git add -p" to separate out parts of files, and commit them as logically independent changes. At this stage, I don't mind separately committing even very scrappy little changes, such as comment typos, as separate commits because I will merge them later. The key is to separate out logically separate kinds of things in the delta from worktree to HEAD. During this I will usually find a few things that are untidy or comments that could be worded better, and add tiny commits for those changes.

The "git add -p" stage is a great time to get some perspective on what logical units were actually needed for the main task, and what else was refactored or fixed in passing, and this "pick up the pieces later" method frees me up to fix things and do small refactors without having to switch context while doing it.

Then the "git rebase -i" stage.

When the adding is done, "git rebase -i" to reorder into a sequence of logical, coherent and explainable changes. Ideally in an order where things still work if they are partially checked out in that order (bisectability), and squash together separate "git add -p" chunks that really must be one logical unit. Also, squashing trivial fixes such as typos in comments, whitespace etc into logical commits.

I may then go back and clean up and flesh out some of the commit messages before pushing the lot upstream for review. Or, in practice, there's usually no review of my work other than testing it, but so that others can at least read through the commit messages and patches to see what was done and how; hopefully learn from it.

The above cycle is usually done about every 1-2 workdays, but it can be longer if there's a tough problem being debugged or a complex new feature (but for big new features a branch is more useful). If something turns out to be too big, though, it doesn't matter because I can always commit any work in progress to a new local branch or stash, and rewind back to a stable worktree.

The final, logically coherent and documented set of commits is very satisfying to push, and rarely contains "junk" commits or unexplained changes, even though what I commit locally at first is often like that. I guess it helps to know Git quite well.

</comment></comment></comment><comment><USER82> I can't think of a specific technique, but one thing that has been very useful has been exploring the use of fuzz-testing.

I'm pretty much adding fuzz-testing to all my current/new projects now. It doesn't make sense in all contexts, but anything that involves reading/parsing/filtering is ideal - you throw enough random input at your script/package/library and you're almost certain to find issues.

For example I wrote a BASIC interpreter, a couple of simple virtual-machines, and similar scripting-tools recently. Throwing random input at them found crashes almost immediately.

</comment><comment><USER83> \- Letting code crash early and hard, deciding what parts of the code i never think about handling \- Dependency Injection (ObjectOriented/Functional), controlling when stuff gets created vs. used \- Accepting to having some level of redundancy in code, using code to configure stuff (wrappers, adapters, factories) and not trying to pull out everything from the start \- Figuring out what parts of what i could test i should test, being fine with just coverage \- Writing a cli for every project \- Learning how to transform code without breaking it for long periods of time

</comment><comment><USER84> The most transformative experience was learning how to retain information better following the Coursera course: Learning How to Learn ([https://www.coursera.org/learn/learning-how-to- learn](https://www.coursera.org/learn/learning-how-to-learn))

I'd not always been the best learner and this taught me how to learn more effectively. Whilst at the sametime accepting the limitations of the brain.

It led me to find the Anki app and combined with more effective learning, it positively affected my ability to be a better programmer.

</comment><comment><USER85> Voraciously eliminating unnecessary whitespace, especially vertical whitespace, was a trick I learned early. Most monitors are wide, not tall. When you can see a whole function without scrolling it's much easier to understand. I'll collapse 'if' statements to a single line in most cases to make it readable left-right instead of up-down. 1TB bracketing is my default unless style guidelines prohibit it. End-of-line comments make it easier to associate functionality with comments and keep code tidy.

<comment><USER86> I personally hate one liners. I used to love them and feel good about how neat the code looked like.

After some time I've noticed that I read the code much easier and understand flow when the indentation is similar to python or go.

Also, I keep my editor on a vertical half of the screen.

</comment></comment><comment><USER35> A lot of really good answers in this thread. I had a great mentor years ago and she really taught me the importance of working cleanly and professionally. Proper code formatting, checking your comments for spelling mistakes, indentation, auto formatting on save, linting, writing solid documentation, rebasing and tidying up your commits to create clear steps that a reviewer can step through. All of the things that often get overlooked but are very important in how you come across as a professional developer.

</comment><comment><USER87> Not mentioned yet

1) Start with consistent naming conventions, notation and structures

2) Rely on autocomplete to simplify typing/readability/programming effort. It's a tremendous force multiplier.

Ninja'd

3) TDD

4) Functional programming

<comment><USER88> Consistent naming does not mean "use the same word for all similar things". It means using one word every time you have the same thing.

Words stop meaning anything when you use it all the time.

Use a thesaurus. Naming everything "handler" or "node" is just naming them "function" and "object".

</comment></comment><comment><USER89> Touch typing. There was a huge difference in speed between the my brain and my fingers. Once removed, it was much easier to make the code flow.

<comment><USER65> You realize you never need anything printed on the keyboard and that's what I use for 10 years now.

Good way to make non programmers laugh too.

</comment></comment><comment><USER90> A divide and conquer approach to complexity; which is turning the complex idea/code into several loosely coupled simple building blocks.

</comment><comment><USER91> I used to just write code fast and forget about it, only slightly caring about the finished product. After having to build and support 2 fairly large applications(for use on a company intranet) I will fully plan, document and test my software. No more clever code, no more quick hacks. I also gave up on Javascript and node.js, and I mostly do c# now because of type safety.

<comment> toper-centage Caveat: sometimes you just want to finish something. Specially when prototyping or creating an MVP, it's important to have a hard focus on the end goal.

</comment></comment><comment><USER92> From iOS perspective changing to clean architecture using VIPER has made our code testable and easier to reason about.

We enforce the architecture as much as possible through protocol conformance, which doesn't always work, but works extraordinarily well for "Views" or "Scenes".

It's taken so much stress out of the development lifecycle for our team.

</comment><comment><USER93> A surprising one is to avoid all kind of jumps (early return, breaks and continues).

A broader one: writing expressions as much as possible. Basically, it means avoiding unnecessary mutations (and jumps).

Then avoiding architecture. Thinking algorithms that process data (instead of "systems") has been transformative.

<comment><USER65> Why avoid early returns? If something doesn't match a condition to process anything in the function, then it's cognitively better to return early and say that case should be left out before possibly doing anything weird in it.

</comment></comment><comment><USER94> Being full stack in mind. You don't need to have full stack skill, but you should have experience working on frontend, backend and Dev/Ops. This can help how you design API between frontend and backend, how you implement your system to ease maintenance.

</comment><comment><USER95> 1\. It's better to implement solidly 8 features then partially implementing 10.

2\. If you can, step through any new code with the debugger. You may find the execution path isn't what you thought it was.

</comment><comment><USER96> I program slowly now, and only program in my head and on paper. Only when I am 100% sure that I have something I can add as a small step to a larger working goal do I touch the code

</comment><comment><USER97> Using "this." for object-scope identifiers and "ClassName." for class-level identifiers.

Greatly improved readability, even though there was a short adjustment.

</comment><comment><USER98> Learning generics and the streaming system in Java. There's a clear improvement in expressiveness and they minimize a lot of duplication and visual clutter.

</comment><comment><USER99> Functional programming with functions as first-class-citizens. And related: asynchronous programming. Both with Node.js, a couple of years ago.

</comment><comment><USER100> using data structures and parsing techniques over "convoluted" algorithms ( pretty much discussed here: [https://lexi- lambda.github.io/blog/2019/11/05/parse-don-t-va...](https://lexi- lambda.github.io/blog/2019/11/05/parse-don-t-validate/) ).

</comment><comment><USER101> 1\. Shifting from solo/modular to team software development

2\. Figuring out unit and integrations tests

3\. Embracing clean code paradigm and SOLID architecture

</comment><comment><USER102> Entity component system as a replacement of OOP. Don't even bother writing a complex app without it.

</comment><comment><USER103> TDD and clean architecture for me.

</comment><comment><USER104> TDD for me. It's frowned upon but has helped me craft elegant code with less bugs .

</comment><comment><USER105> KISS + YAGNI + constant refactor. Code is a means to an end and I suck at futurology.

</comment><comment><USER106> Metaprogramming in Common Lisp.

</comment><comment><USER107> JSIO - just shit it out. Stop worrying about it being perfect and get it done

</comment><comment><USER108> Learning ReactiveX. Never wrote classical imperative code since

</comment><comment><USER109> start with explicit requirements to the system you are building. so obvious, but so neglected! i blame the typical belief in one's omniscience.

</comment><comment><USER110> two things 1) leading commas in SQL 2) RAISE an EXCEPTION or RETURN --no more `else` statements in code

SELECT this , that , something FROM table

takes a while to get used to

<comment><USER65> What is the point of selecting specific columns unless some include large text or blob to consume unnecessary IO?

<comment><USER111> Here's a few reasons why when you finalize a query, you select only the necessary columns (non-exhaustive list):

\- Reduce uneccessary IO / resource strain, as you said

\- Predictability; consuming program receives data in the same order, every time

\- If the DBA adds additional columns to the table, it doesn't hose downstream consumer processes

\- Easier to debug if some problem does arise.

\- Clarity, if you are only using a few columns from a large table. I might just be getting old though ;)

Data should be served in the simplest, most robust manner.

It should be easily consumed by other services, with little extra effort from the programmer.

If you use Select *, you either accept the possibility of it breaking unexpectedly, or have to write logic within the consuming service to deal with that. If the problem can be eliminated by the DB/query itself, it should be.

<comment><USER65> I understand how clarity may provide the programmer's intention of the query but I'd rather just pick all, so that change in code below doesn't suddenly break because you didn't sync the columns to be fetched.

> \- Predictability; consuming program receives data in the same order, every > time

For any joined table, you can specify table name for each '*' or add an alias last to keep the column you need instead of writing it all.

Seems specifying each column name isn't in any way crtitically bad when you can't select all but a column in SQL, which is a deficiency in the language.

</comment></comment></comment></comment><comment><USER112> starting to use map, reduce, filter on Arrays, and arrow functions

</comment><comment><USER113> Learning Doxygen

</comment><comment><USER114> Buy oxycodone online, Oxy, sold under name OxyContin among others, is an opioid medication used for the treatment of moderate to severe pain. it's usually taken orally, and is out there in immediate release and controlled release formulations From 24hrsonlinestore [https://24hrsonlinestore.com/product-category/buy- oxycodone-...](https://24hrsonlinestore.com/product-category/buy-oxycodone- online/)

</comment><comment><USER114> It is like instant release oxycodone, morphine, and hydromorphone within the management of moderate to severe cancer pain, with fewer side effects than morphine. Buy Oxycontin Online without prescription here legally.From 24hrsonlinestore. [https://24hrsonlinestore.com/product-category/buy- oxycontin-...](https://24hrsonlinestore.com/product-category/buy-oxycontin- online/)

</comment><comment><USER114> Buy Percocet Online without Prescription. This combination medication is employed to assist relieve moderate to severe pain. It contains an opioid (narcotic) pain reliever (oxycodone) and a non-opioid pain reliever (acetaminophen). Oxycodone works within the brain to vary how your body feels and responds to pain. Acetaminophen also can reduce fever. From 24hrsonlinestore [https://24hrsonlinestore.com/product-category/buy- percocet-o...](https://24hrsonlinestore.com/product-category/buy-percocet- online/)

</comment><comment><USER114> It is like instant release oxycodone, morphine, and hydromorphone within the management of moderate to severe cancer pain, with fewer side effects than morphine. Buy Oxycontin Online without prescription here legally.From 24hrsonlinestore [https://24hrsonlinestore.com/product-category/buy- hydrocodon...](https://24hrsonlinestore.com/product-category/buy-hydrocodone- online/) [https://24hrsonlinestore.com/product-category/buy- oxycodone-...](https://24hrsonlinestore.com/product-category/buy-oxycodone- online/) [https://24hrsonlinestore.com/product-category/buy- oxycontin-...](https://24hrsonlinestore.com/product-category/buy-oxycontin- online/) [https://24hrsonlinestore.com/product-category/buy- percocet-o...](https://24hrsonlinestore.com/product-category/buy-percocet- online/) [https://24hrsonlinestore.com/product-category/buy-ambien- onl...](https://24hrsonlinestore.com/product-category/buy-ambien-online/) [https://24hrsonlinestore.com/product-category/buy-xanax- onli...](https://24hrsonlinestore.com/product-category/buy-xanax-online/) [https://24hrsonlinestore.com/product-category/buy- adderall-o...](https://24hrsonlinestore.com/product-category/buy-adderall- online/) [https://24hrsonlinestore.com/product-category/buy- dilaudid-o...](https://24hrsonlinestore.com/product-category/buy-dilaudid- online/) [https://24hrsonlinestore.com/product-category/buy-lortab- onl...](https://24hrsonlinestore.com/product-category/buy-lortab-online/) [https://24hrsonlinestore.com/product-category/buy-codeine- on...](https://24hrsonlinestore.com/product-category/buy-codeine-online/) [https://24hrsonlinestore.com/product-category/buy-valium- onl...](https://24hrsonlinestore.com/product-category/buy-valium-online/) [https://24hrsonlinestore.com/product-category/buy- adderall-x...](https://24hrsonlinestore.com/product-category/buy-adderall-xr- online/) [https://24hrsonlinestore.com/product-category/buy-adipex- onl...](https://24hrsonlinestore.com/product-category/buy-adipex-online/) [https://24hrsonlinestore.com/product-category/buy-norco- onli...](https://24hrsonlinestore.com/product-category/buy-norco-online/) [https://24hrsonlinestore.com/product-category/buy-vicodin- on...](https://24hrsonlinestore.com/product-category/buy-vicodin-online/) [https://24hrsonlinestore.com/product-category/buy- methadone-...](https://24hrsonlinestore.com/product-category/buy-methadone- online/)</comment>


Putin signs law that prohibits technology providing access to banned websites - <AUTHOR> http://www.reuters.com/article/us-russia-internet-idUSKBN1AF0QI <comment><USER2> Waiting for the "can't stop me using VPNs L0Lz" comments..

VPN use for a free and open Internet is abnormal - not a long term solution to issues like censorship.

They're already out of reach for normal users. Companies will offer better ways to block or detect usage for lucrative gov contracts. Simply making it illegal will detour most people.

I don't understand why VPN crackdown is thought to be impossible. ISPs have a good idea of what normal traffic looks like and a very good map of the Internet. The Internet may be big. To an ISP, is it really that big? Netflix, Google, YouTube, Dropbox etc are normal. Your companies VPN IP are normal.

That's unusual! Open connection to a DNS port? sending large amounts of data? its encrypted? No one has accessed that digital ocean IP before on this network! Lets throttle, interrupt it periodically or flag that user. Someone else sending large amounts data to a known VPN IP range? block, throttle, close connection, police knock on door etc.

policy change, not more VPNs.

<comment><USER3> Exactly. It baffles me how people think political problems can be solved with technological workarounds. What's next, being gay is illegal again so "just hide your relationship lulz"? Sometimes, I feel like a part of the crowd here is like a bunch of toddlers who try to work around some rules the parents made and feel clever at the same time.

<comment><USER4> A responce to this by a barrister friend of mine. Re-phrased because I don't remember it verbatim:

"We've only been trying technological solutions for the past hundred years and look at what they've achieved. We've been trying political solutions for thousands of years."

Though, I agree that we need to be aware of the limitations we and our systems impose on technical solutions. That said, perhaps we should play to our strengths?

<comment><USER5> >We've only been trying technological solutions for the past hundred years and look at what they've achieved

Holocaust, Nazi Science, Atomic bombs, Stalin's industrialization, mass surveillance, cold war? Man, 20-th century was such a mess, technology only made that mess more messy, and good stuff was achieved by political discussion, dialog, not because of new technologies (although technologies makes both dialog and murdering much easier)

</comment></comment></comment></comment><comment><USER6> The bigger news, IMO, is that Putin also signed the messenger law. To remind, this demands that all IM services that operate in Russia must provide technical means to de-anonymize the users, by linking their accounts to a phone number, a service contract number where the contract has identifying information, or other similar scheme. They're also required to be able to block any materials censored in Russia. If these are not complied with, the regulatory agency can block the service entirely.

<comment><USER7> Mesengers are also required to block information that is being spreaded in a violation of russian laws.

</comment></comment><comment><USER8> This article is very light on the details, but the fact that someone has actually gone ahead and done this will mean other leaders / politicians will now be thinking - "oh right, this is actually something we may be able to do."

Another few feet down that slippery slope.

<comment><USER9> > _the fact that someone has actually gone ahead and done this[...]_

Well it's not exactly a novel idea. China, North Korea, Iran, Iraq, Oman, Turkey and others have implemented bans or heavy restrictions for quite some time now.

<comment><USER10> I'm sure it's coming to the US to ensure that subscribers aren't avoiding ISP rate limits.

<comment><USER11> First, no, this isn't coming to the US.

Second, this doesn't make any sense. How would you avoid rate limits by using a VPN?

The ISP is still sending and receiving all of that data for you.

<comment><USER12> Step 1) Rate limit high bandwidth use sites to discourage use

Step 2) Block VPNs to prevent circumvention of your rate limts

Step 3) Profit?

I don't see this happening, but I think that's what the comment you were responding to is suggesting.

<comment><USER13> Why not just rate limit VPNs?

<comment><USER14> You are not thinking politically enough.

If I rate-limit VPN outright, I'm an ugly dictator bent on destroying privacy.

If I rate-limit popular bandwidth-intensive traffic, I'm just a guy trying to keep network usage fair for every user. If that involves limiting VPNs as well, they are simply collateral damage, the end justifies the means, and I'm still a good guy.

</comment></comment></comment></comment></comment></comment></comment><comment><USER15> This is hardly surprising. Putin is an autocrat who wants to become a full- scale, ruler-for-life dictator. And so to achieve that end he has been steadily chipping away at freedoms and human rights.

<comment> c-smile For the brevity: "the law, already approved by the Duma, the lower house of parliament" so it is a collective decision of Russian lawmakers.

<comment><USER16> Meh, their collective decision is to follow Putin's orders. Gosduma's sessions are streamed online but they still don't give a fuck and play games on smartphones, read newspapers, chat and even don't show up at all so others have to vote for them[1]. No wonder most those draconian laws are passed with 90%+ approval.

[1] [https://www.youtube.com/watch?v=Usmji_xRe0U](https://www.youtube.com/watch?v=Usmji_xRe0U)

</comment></comment></comment><comment><USER17> Everytime I read posts about VPNs and ban attempts, I can't resist to link to the Streisand project: [https://github.com/jlund/streisand](https://github.com/jlund/streisand). Used that when there were crackdowns on VPNs (Astrill, Express, etc.) while I was living in China and it never disappointed.

</comment><comment><USER18> Duh? If it's forbidden to access certain websites in a country then logically so is any service that enables such access.

VPN services can't exactly hide behind being an impartial carrier since they sell themselves as tools precisely for circumventing censorship, tracking, region locking, and providing anonymity.

I've said it before and I'll keep saying it, this is a political problem that can't be solved through more tunnels.

<comment><USER19> > VPN services can't exactly hide behind being an impartial carrier since they > sell themselves as tools precisely for circumventing censorship, tracking, > region locking, and providing anonymity.

I really wish some VPN providers would try advertising themselves as desirable technical solutions to the technical shortcomings of many ISPs -- e.g. my ISP here doesn't give me native IPv6, and has a crappy last mile. OpenVPN-over-UDP helps with both.

VPNs can also simply protect against attacks on public networks in e.g. cafes, libraries, airports.

> this is a political problem that can't be solved through more tunnels

Yes, but they help in that they increase people's headroom for informing and acting.

<comment><USER19> > VPNs can also simply protect against attacks on public networks in e.g. > cafes, libraries, airports.

To expand on that, you could argue this for any network, even home and company networks -- who knows which of the many windows computers and android phones of my colleagues/family are bots?

<comment><USER20> Not really against remote code execution type exploits, more like password/session stealing attacks.

</comment></comment></comment></comment><comment><USER21> "The strictness of Russian laws is compensated by the optionality of compliance."

<comment><USER16> Every time Gosduma passes some law restricting citizen's freedom for the sake of security we have morons bleating "Don't worry, guys, the strictness of Russian laws is compensated by the optionality of compliance".

Yet in the past few years we had hundreds of people jailed and/or fined for the content they posted on social media or even for reposts and likes. Yet the morons do not learn. Can't wait for Yarovaya's law to come in full effect.

</comment></comment><comment><USER22> I do not make political comments because it seems unsafe. So I am not commenting about Russia here or any other country.

[https://www.reddit.com/r/darknetplan/](https://www.reddit.com/r/darknetplan/) and [https://www.reddit.com/r/Rad_Decentralization/](https://www.reddit.com/r/Rad_Decentralization/) may be interesting from a purely technical perspective.

</comment><comment><USER23> The important thing is that people are not charged in crime if they try to circumvent the bans. So it's still race in technology field. They'll ban VPNs, we will find ways to hide our VPNs. As long as regular user won't be prosecuted for trying to access prohibited information, those laws doesn't matter much IMO.

</comment><comment><USER24> Link to official document: [http://publication.pravo.gov.ru/Document/View/00012017073000...](http://publication.pravo.gov.ru/Document/View/0001201707300002?index=0&rangeSize=1)

</comment><comment><USER25> I'm really curious how this affects HN users in Russia now. Is this a concern for you all? Or is it just a blip? Do you think it affects your less technical friends and family in any way?

</comment><comment><USER16> Funny thing: two month ago, when Ukraine blocked access to Mail.ru Group's (which is owned by Kremlin linked bussinessman Usmanov) websites (Vkontakte, Odnoklassniki, russian 4chan (sort of)), state-owned TV ran a couple of infomertials teaching people how to bypass blocks using VPNs and anonymyzers.

</comment><comment><USER26> The important part of this news has been omitted. FSB will monitor compliance with the law.

</comment><comment><USER27> All the more farcical was his recent claim in Q&A session with school students, that Russia has no Internet censorship.

</comment><comment><USER28> What if I use a VPN to access my company's network?

<comment><USER29> That case is explicitly allowed by this law.

<comment><USER30> So much like China then. Fine for email getting through on firstname.lastname@example.com from the Chinese office, just an added hassle for the IT department to setup.

</comment></comment></comment><comment><USER31> Wondering if this document essentially states that a Socks 5 proxy is banned for the same reasons.

<comment><USER32> If you start advertising it as a way to bypass blacklist and open it to the world, it will probably get banned.

</comment></comment><comment><USER33> Is it also means that no tor-browsing, No wikileaks from Russia ?

Did this affect to ransomware collector too?

</comment><comment><USER34> Wow, sounds like he doesn't like net neutrality either.

</comment><comment><USER35> what are they going to ban next https?

</comment><comment><USER36> I don't think it's possible to reliably enforce this ban.

<comment><USER19> We need something that tunnels over HTTPS/Websockets, and utilizes domain fronting [1] with e.g. Google Cloud or Azure. Ideally it'd also work over WebRTC in unreliable mode, but I'm not sure that works with domain fronting.

[1] [https://bamsoftware.com/papers/fronting/](https://bamsoftware.com/papers/fronting/)

// edit: why the downvotes?

<comment><USER37> > We need something that tunnels over HTTPS/Websockets, and utilizes domain > fronting [1] with e.g. Google Cloud or Azure. Ideally it'd also work over > WebRTC in unreliable mode, but I'm not sure that works with domain fronting.

The Tor Browser already comes with the meek transport[1] that uses domain fronting, one with Amazon and the other with Azure. There's also a recent PT titled snowflake[2], that uses WebRTC to connect to short lived proxies that people can make by just running some JS code in their browser, it's currently available in the alpha builds for Linux and Mac.

Note that meek is expensive, and the Tor Project pays for each byte. It should only be used when no other PT works.

[1] : [https://trac.torproject.org/projects/tor/wiki/doc/meek](https://trac.torproject.org/projects/tor/wiki/doc/meek)

[2] : [https://trac.torproject.org/projects/tor/wiki/doc/Snowflake](https://trac.torproject.org/projects/tor/wiki/doc/Snowflake)

<comment><USER6> The problem is that any website that allows domain fronting is itself illegal under this new law, and can be blocked on those grounds.

<comment><USER19> The idea of domain fronting though is that the fronting website is "expensive" to block that the censor won't do it.

As with everything else, this isn't a 100% technical solution, it has a political component of how expensive the fronting website is to block.

<comment><USER7> Many users in Russia use russian social network (Vkontakte), russian email services and russian search engine (Yandex). There is also popular dating service (Badoo). So Google and Facebook can be blocked without causing much trouble. But there is no good replacement for Youtube and Android Store (yet).</comment></comment></comment></comment></comment></comment>


Android: Fragmentation? More like Fragmentawesome. - <AUTHOR> http://replicaisland.blogspot.com/2010/01/fragmentation-more-like-fragmentawesome.html

<comment><USER2> _That's not to say it was playable on all of them--the Covia SmartQ5, for example, has no keyboard, trackball, or even orientation sensors, and you have to use a stylus on the screen (it also has no GPU so the game runs extremely slowly). And some devices (like the LG Eve) have directional pads that are really poor for games._

In other words: Fragmentation is a bit of a problem on the Android platform.

Sure, your software doesn't crash on any Android hardware. Good for you! But there's a big difference between _does not crash_ and _is useable_.

Fortunately, your software is also usable on "average" Android hardware. Great! But there's a big difference between _usable_ and _useful_ , and an even bigger difference between _useful_ and _elegantly designed_.

The problem is least-common-denominator design: You can't assume a hardware keyboard, and you can't assume a software keyboard, and you can't assume multitouch, and you can't assume an orientation sensor, and you apparently can't assume a minimum level of graphics performance. So you design something that runs well on some hardware, but not so well on others, in a way that's not necessarily easy for a potential software customer to understand or anticipate in advance. And you're careful not to accidentally take too much advantage of some hardware feature that isn't widely available, lest you box yourself into a niche.

<comment><USER3> You can target specific hardware features (keypad, touchscreen, etc.) in the package manifest. The Android Market uses this to filter out apps that can't be run on your phone.

It's not a really a matter of 'least-common-denominator' design; it's a trade off between how wide of an audience you want to reach and how many features you want to use.

</comment></comment><comment><USER4> I can deal with hardware fragmentation, but I am still concerned with software fragmentation. For instance, we (Bump) have different contacts code for Android 1.x, Android 2.x (yes they were supposed to be compatible), and HTC. Bump is not stable on several devices that have changed the contacts subsystem but that we do not have time to support individually (and we can't compile to install on only a subset of known working devices). And I am sure there are problems on handsets we don't know about (there seems to be a new Android handset that we have never heard of on our servers every week). This may be a bigger problem for us since we are so tied to the contacts database and it seems to be a popular subsystem to change but an unpopular one to developers. In the end it means more time bug fixing than feature developing which is too bad.

That being said we are extremely excited about the openness of the platform and the features that enables us to build. :)

</comment><comment><USER5> The fears about Android fragmentation are, in some cases, overblown. The point that concerns me is that this guy has, or has gotten his hands, on every one of those Android devices.

This is a feat that most Amateur developers cannot afford to pull off. It is, however, a feat that any iPhone developer can manage (there are only a handful of devices)

My fear, with platform fragmentation, isn't for the people who do this for a living (I do, and platform fragmentation is just one of the many problems we already have systems in place to handle) My fear is for the people who want to do it as a hobby.

So for us professional folks, the platform fragmentation issue is very over- blown. But if you're a one-man shop who wants to make some mobile software in his/her spare time (for which there is a thriving market on the iPhone) then this is actually a huge hurdle to get over.

Sure, if you have access to all the Android devices, you might or might not have to make code changes to support specific handsets (yet). But if you can't get your hands on every Android device...and you want to develop a paid app in the evenings, get ready for a deluge of angry users for whom your application is just too slow to work. Further, these will be problems you won't be able to debug _until_ you get that email from an angry user.

</comment><comment><USER6> > _"The cool thing is, Replica Island ran on all of them without complaint. > That's not to say it was playable on all of them"_

But wait, isn't that the entire _point_ of a standardized platform? The ability to guarantee some minimum of user experience quality?

The splintering problem is real, and add it to the propensity for OEMs to add all kinds of (mostly poorly thought out) UI tweaks to Android, and what you have is something that really shouldn't be advertised under a single brand name.

<comment><USER7> No, a game is an entirely different beast. Specially when using OpenGL. You start breaking things when you don't use the higher level classes and functions. Meaning, the lower you get, the more you'll have to write code to support different platforms.

The Android API is indeed standardized accross all platforms. Also, what's standardized is the java methods you use and the libraries provided.

Making a game, usually means you're rendering to hardware and you have to handle things like screen resizing, etc, yourself.

However, there isn't a standard UI definition yet like the iPhone has. If that's what you mean.

<comment><USER6> Yes, yes, I get the point about not having to write a huge amount of code to account for multiple platforms because of, say, differing levels of OpenGL support.

But IMHO this is a pretty narrow definition of cross-platform success. In this case shipping this wonderful cross-platform package on multiple phones results in many customers being _unable to play_ \- either due to excessive poor performance or just plain lack of input devices.

So, what's a dev to do at this point? Well, that's easy, start writing device- specific code. That phone has a convoluted direction pad that doesn't work for the game? Well, switch to something else. This one has a poor graphics chip? Let's gracefully degrade quality to get some speed.

But wait, we just nuked the primary benefit of standardization!

This is my original point: when you standardize it can't just be half-assed, and that's what I see on Android right now.

<comment><USER7> Well, that is a fundamental problem, not a problem specific to Android. For years, pc game makers have complained about the costs of developing for PC due to varying support from graphic cards.

So, you end up having to write various rendering engines with a lot of conditionals and the ability to change the setup via a menu.

Now, add to this a crap-load of varying input devices, single/multi touch screen, and you've got the same problem that PC game makers have but worse.

It's not the API that needs to be standardized, but the hardware. The only reason that the iphone doesn't have this problem is because they only have 2 or 3 devices and they're all pretty standard hardware-wise.

Most console game makers site the reason of developing for a console is the standardized hardware. However, you get lock-in with that. So, it really is a fundamental flaw/problem.

Beside, that's why programming languages have 'if/else'.

Yes, you have to do a lot of testing on your end, but you're not stuck to one platform.

Also, if you want to program your run of the mill program, then the standardized API is more than capable. But like I said, if you're making a (high-performance) game, you have to sacrifice things, this is true for ALL platforms.

<comment><USER8> A good point. Amateur developers can't afford multiple devices to test on (Android), but they also can't afford to be rejected indefinitely (Apple). So really, there is no good platform for developing mobile games on.

</comment></comment></comment></comment></comment><comment><USER9> Saw the author of Replica Island talk at Google IO. He's worked for nintendo among other handheld companies and really knows his stuff. He works for Google now and developed Replica Island as his 20% project. I'm glad that his software runs well on all the various android capable devices because if he can't get it portable, chances are not many can. One thing not mentioned in this particular article is the fact that he's not only open sourcing replica island but also the engine he's developed to create it, so it should make game development easier on the rest of us.

</comment><comment><USER10> The idea that the iPhone platform doesn't suffer from "fragmentation" is wrong, it does and it's getting worse. Developers already have to deal with iPhone 3Gs, iPhone orig, iPod touch, and now the iPad. I'm sure there will be many more devices coming down the pike over the years, each with different capabilities.

<comment><USER11> At least you don't have to worry about IE6.

</comment></comment><comment><USER12> I agree. The fears about Android fragmentation are overblown. There is a huge continuum between, say, the iPhone and J2ME on the other end.

</comment><comment><USER13> Is your app 1.6+ or just not in the Android Market? I searched for it and can't find it. (stuck on 1.5.x)</comment>


Open Q&A: Moving 65,000 Microsofties to DevOps on the public cloud [video] - <AUTHOR> https://www.youtube.com/watch?v=M7mHOhUQjmU <comment><USER2> Here's a free automated transcript if you don't want to watch

[https://scribie.com/transcript/e32d5f58327045a18842116330a91...](https://scribie.com/transcript/e32d5f58327045a18842116330a914d47070905e)</comment>


Ask HN: Anyone using Digital Ocean managed databases for production? - <AUTHOR> I was wondering if anyone is using their managed databases for production and what's your review of their reliability?   They launch in Feb but I couldn't find any reviews online.  I figured HN will be the right audience to ask. <comment><USER2> I've been using CouchDB for the past few years on new development so I've not used them, but I might if they offered CouchDB as an option.

I like the idea of it. Right now I make a snapshot of a server with CouchDB installed and mostly configured when I want to spin up a new one. I have it on a $10 per month vps so I can scale up from there.

</comment><comment><USER3> I've been migrating my site to use Digital ocean managed databases but it seems the performance might take a hit for my tables that have more than a million rows.</comment>