Hacker News new | ask | show | jobs
by jcranmer 1762 days ago
I'd disagree with this assessment. In my own experience, I've found that competitive programming is useful in teaching a few skills. However, the apparent point of competitive programming--building up a good repertoire of advanced algorithms and data structures--is pretty close to useless. In my professional career, the most complex algorithm I've ever had to code myself is ... union-find (on like two occasions), with a custom BFS/DFS being the most common algorithm I reach for. Almost everything you will ever need is already implemented in a library for you to reuse, and even reaching for that library is pretty rare.

No, the most useful impacts of competitive programming has to do with actually teaching you how to program effectively. The most important advice I got for competitive programming was walk away from the computer. The biggest trap when programming is to write code before you know how to solve a problem, as this will tend to encourage you to spend all of your time making no progress towards actually getting a solution. The tale of the Sudoku solver [1] is salutatory here.

Another useful skill is how to debug algorithms. Debugging a competitive programming problem usually amounts to taking an input that fails and trying to understand where in the algorithm (as expressed in poorly written and commented code!) the mistake is. Doing so under the time pressures of a competition also requires being able to rapidly decide if the algorithm is wrong, or it is the implementation is wrong, as well as figuring out where the most likely places for errors are likely to creep in. This kind of experience translates very usefully into professional programming, and debugging is a skill that seems to be poorly acquired by most young programmers.

When competitive programming hits the tier where you need to memorize all of the advanced data structures and algorithms to do better, that's where further improvement in the leaderboards no longer translates to better programmers IMHO. But there's still a lot of skills that need to be acquired to move up competitive leaderboards before you reach that point.

[1] https://ravimohan.blogspot.com/2007/04/learning-from-sudoku-...

8 comments

To me competitive programming doesn't make you a good software engineer.

It will teach you bad practices, like abusing of dangerous programming constructs or data structures to optimized the code or just save some typing while writing the code (like one letter variable names, macros, etc), global variables and state in the program, not using common design patterns, not using OOP, not documenting the code and in general writing code that is difficult to maintain because it's not well engineered. You would say that a person would do that only in competition, but in my experience I saw that is not true, because if you learn to program a particular way, you would do the same also in other contexts.

Also, the competences that you will acquire by doing competitive programming are next than useless: in the real life I've yet to encounter a situation in which I have to use an algorithm used in a competitive programming competition (yes, I did them with even good results). And if you need them, you don't implement them yourself, but you use a library that manages all the work. The Dijkstra implementation that most competitive programmers use is a toy implementation, not something that you would never use in any sort of enterprise software.

The fundamental skill that competitive programming doesn't teach you, that to me is the most important thing for a good software engineer, is using Google to find documentation online. Most of the errors I see by young software engineers could easily been prevented if they did a simple Google search and read the official documentation (not a random blog post)! And yet in competitive programming you are forbid to use the internet, to me it's stupid, it's like requiring to write code with a typewriter just because in the past keyboards and monitors didn't exist.

> It will teach you bad practices, like abusing of dangerous programming constructs or data structures to optimized the code

If someone can't weigh up the pros and cons before using a particular technique, they're going to be a bad software engineer anyway. The field is all about making the right tradeoffs in particular situations, and blindly following current generalised "best" practices without really understanding what they're meant to be better than is going to get you into trouble too.

Breadth of knowledge and experience is a great thing. If you follow from the start that OOP is the right and only way to do things for example, you're not going to have a feel for when it gets in the way. Actually knowing what it's like and what happens when you do something the "bad" way is very valuable experience too e.g. you're going to be a better coder if you'd had to battle with buffer overflows and memory leaks in C in the past even if now you only use Java and Python.

This kind of logic really bugged me after moving from academia into industry. In academia for example, it's usually the right choice to make quick and dirty prototypes to get the results you need for a paper and not burn time over-engineering it. This doesn't mean I don't how to clean up code. I would also say it gives me more experience on avoiding over-engineering compared to someone that tries to copy best practices all the time.

> It will teach you bad practices, like abusing of dangerous programming constructs or data structures to optimized the code or just save some typing while writing the code (like one letter variable names, macros, etc), global variables and state in the program, not using common design patterns, not using OOP, not documenting the code and in general writing code that is difficult to maintain because it's not well engineered.

These are not “bad practices”. Many of them are bad in certain common contexts, but literally none of them are bad in throw-away code used in the course of root-cause-analysis of problems in other code basis, and most are useful in many parts of proof of concept code. In working on production systems, most of the volume of code I produce isn’t production code, and has a very different set of constraints than production code.

And given history on HN, whether “not using OOP” is ever a “bad practice” is probably a whole discussion of its own.

And if the proof of concept does work, you take all the code that you written, throw it away and rewrite it? Probably not. I've seen too much "proof of concepts" go into production because they worked. Except when someone else had to actually maintain the thing and the person that wrote it leaved the company 1 year before.

There is not proof of concept code to me. Every line of code that I write professionally I write it to the higher standards. That doesn't mean over engineering it, but it means writing it in a way that is maintainable and understandable by others. I can compromise on functionality, of course in a proof of concept I implement only the things that I need, but not on code quality.

Even if I have to write a script that I know it will have to be used once and never again I will write it in a good way, you never know if you need it again, if a coworker does need to do a similar thing and you can just give to him as reference, or if you can take pieces to do other things.

Competitive programming doesn't teach you only algorithms and data structures, it teaches you to think very clearly about program flow, edge cases, debugging etc., points which you seem to just gloss over from the comment you're replying to, just to justify a conclusion you had already reached.
definitely, alot of the "algorithms" in leetcode practice look deceptively simple on the surface, but deep down, there are lots of edge cases and are good practice to think deeply about them

on the otherhand, they can also encourage messy and hard to maintain code, or super efficient implementation can be brittle which you dont want in production unless there is a really really good reason...

if "competitive coding" also judge on other criteria such as clarity, readability and maintainability, in addition to efficiency and "correctness" that would be great imo

Not using design patterns seems like pure upside.

I don't think it's reasonable to use a library for most things one implements in competitive programming, because the work of getting your problem instance in and out of the library is probably greater than the work of writing the solution yourself, in code length and in runtime. As an example, the only time I ever needed to use A* was on a state space that was larger than the main memory of any computer on Earth. I don't expect that I can download a library that does A* on abstract state spaces not fully present in RAM for me, but I can just bash out my own A* that's fused with the definition of the state space. Maybe this is so easy to do partially because I did a bunch of programming contests in which I wrote BFSes that were fused with the definitions of graphs.

> I don't expect that I can download a library that does A* on abstract state spaces not fully present in RAM for me

I'm sure this does exist! A functional library where you pass in, not a pointer to the set of all nodes, but some functions to get the node with the current lowest distance and to update the distance of a node.

Not saying it was the right thing for your use case, but don't rule it out. Even in languages that aren't strongly FP - eg it happens a lot in Go due to the lack of generics, and if you squint, the C++ STL is kind of like this.

It's not. Because then someone else has to maintain the code you written, and if he cannot see any common pattern in your code he will make 10x the effort to understand the code, that means wasting time and thus loosing money for the company.
Is this about the design patterns or the A*? Design patterns are a mix of techniques to pay complexity make code extensible that usually will not ever be extended and workarounds for the deficiencies of a specific family of languages. An example I posted about previously is that you could spend hundreds of lines across a dozen files implementing double dispatch, or you could use dramatically less if you aren't implementing it in terms of a language-provided dynamic-dispatch-on-the-first-argument-only primitive.

It is nice to write code that can be understood by the next reader. To explain what our invariants are and what we are up to, we have comments.

Not using OOP is a bad practice? That's a hard sell.
> It will teach you bad practices

Agreed, everything that makes good contest code is a bad thing for production code. In a contest you just want to hack together something as quickly as possible, it only needs to work once, will never be maintained or documented.

It's all good fun as a sport, but nobody should consider it relevant to working on production code.

> Almost everything you will ever need is already implemented in a library for you to reuse, and even reaching for that library is pretty rare.

Developers who have no interest towards algorithms will not even have the skills required to identify opportunities where algorithms should be used, much less the skills required to identify which algorithm should be used. For example, I had a conversation here on HN a few weeks back where somebody said that they will not need to learn anything about sort algorithms, because they can just google bubble sort and copypaste it if they need sorting at work. And that's how you end up with 5-minute load times for GTA.

> somebody said that they will not need to learn anything about sort algorithms, because they can just google bubble sort and copypaste it if they need sorting at work

In this example, the missing skillset is not memorizing a book of algorithms as you seem to imply.

If performance isn't good for the use case, they should be profiling the code for root causes. Which will lead them to identifying the sort operation as a problem. At which point they can search for faster sorting algorithms which will quickly lead them to a better solution and libraries containing them (or even how to implement, if they're operating in a constrained environment where no libraries exist).

I don't want to hire software engineers who have memorized the algorithm book, that's not useful to me. I want to hire those who can apply discipline to the work, such as (in this example) be aware of performance and know how to analyze it and find solutions.

> In this example, the missing skillset is not memorizing a book of algorithms as you seem to imply.

No it's not. As I said in the sibling comment, competitive programming (or algorithm skills in general) isn't about memorizing algorithms, it's about developing skills that allow you to understand and create algorithms to solve problems. In this case, it would be sufficient to understand why looking up a phone number in a phone book should inherently be a O(log n) operation, not a O(n) operation.

> I don't want to hire software engineers who have memorized the algorithm book, that's not useful to me. I want to hire those who can apply discipline to the work, such as (in this example) be aware of performance and know how to analyze it and find solutions.

So we're discussing a hypothetical developer who has "no interest towards algorithms", and you're imagining that this person is going to run a profiler to troubleshoot a performance issue and subsequently optimize algorithms to fix said performance issue? This sounds like a fantasy to me. If you want skilled software engineers who have the ability to troubleshoot and optimize algorithms, you probably need someone who has some kind of interest towards algorithms.

> So we're discussing a hypothetical developer who has "no interest towards algorithms", and you're imagining that this person is going to run a profiler to troubleshoot a performance issue and subsequently optimize algorithms to fix said performance issue? This sounds like a fantasy to me.

Our experiences are vastly different then. Nothing fantasy or hypothetical about this. Every great software engineer I've worked with is like this.

Profiling and identifying hot spots is an every-week occurrence in the field, very pragmatic and valuable work. Theoretical algorithms only happen on interview whiteboards, not on the job.

You're arguing that someone who doesn't care to learn anything about algorithms is going to do a good job at optimizing algorithms.
No, not at all!

I am saying that being able to regurgitate an implementation of quicksort (to keep on the sorting example) onto a whiteboard is not a useful skill. There will never be a need to do this in a real life job.

The truly useful skill is being able (and interested!) in using a profiler to find bottlenecks, identify what is being done there (let's say, sorting) and being able to find a better replacement algorithm in the literature and change to code to use a well-optimized implementation (which exists already in just about every ecosystem, no need to reimplement it from memory).

>And that's how you end up with 5-minute load times for GTA.

was it actually the reason?

Different game, different problems, but I've spent a fair amount of time looking at the source for Civ IV's core game DLL (released by Firaxis for modding).

It was fairly simple to get significant performance gains (without changing AI behavior or results) just by applying straight forward changes. Eliminating macro's hiding chained method calls. Saving the result of nested calls, e.g. save a pointer to the game map instead of making the same call repeatedly in a tight loop.

There's a huge difference between "premature optimization" and just sloppy coding. Too many take the idea too far that the (C++) optimizer is better than you'll ever be and that you should let it do it's job. Yes, the optimizer is/can be very good and can make surprising and unintuitive changes to make code faster. But, it's not a miracle worker. Optimizers work best on clear, concise, code with minimal data dependencies.

FWIW, back on my Core I2 Duo, a single late-game turn on a huge map could take upwards of 30 minutes to complete, most of that spent in AI's turns. I was able to get it down to about 5 minutes without too much effort. Along the way, RAM usage also plummeted. If I had full access to the source, I could have made even greater gains that would have broken the DLL ABI...

Sadly, I never released my changes anywhere (and I only had local SVN at the time), and they haven't survived my PC upgrades since.

No they weren't using bubble sort. When parsing JSON, they were calling sscanf (which internally was calling strlen). In addition, those parsed objects were stored in an array which was iterated through on each insert to check for duplicates.

There was an article [0] and subsequent discussion on HN [1] about reverse engineering and patching the binaries.

[0] https://nee.lv/2021/02/28/How-I-cut-GTA-Online-loading-times...

[1] https://news.ycombinator.com/item?id=26296339

No? If you check your own sources, you will find that this is precisely the type of case where a developer is using an O(n^2) algorithm out of ignorance when they should have been using a faster algorithm, and that the high load time was caused by this mistake. The time complexity for bubble sort is also O(n^2), compared to O(nlogn) for reasonable sort algorithms. I don't really understand how you take all this information and then conclude "no".
The way accidental O(N²) comes about isn't "I'm intentionally using an algorithm that's O(N²)" (e.g., bubble sort), but "this method that I think is O(1) is actually O(N)" (whether because it's badly designed or just badly documented).

It's really not in the same vein as using bubblesort instead of a mergesort: failures aren't in algorithm design but in the actual mechanics of the implementation that is ancillary to the algorithm itself.

> failures aren't in algorithm design but in the actual mechanics of the implementation that is ancillary to the algorithm itself.

Strongly disagree. The GTA developer basically designed an algorithm which loops items in an array, and for each iteration in the loop, they parse the entire JSON that contains all items, and they do lookups for duplicates from an array instead of from a set. This is like a quintessential example of an algorithm design failure, so it seems weird to me that you're not attributing it to a failure in algorithm design, but instead you pass the buck to creators of the library that the developer was using. Sure, the library methods could have been documented better, or implemented differently, or whatever, but that seems like an entirely unrelated discussion.

> "this method that I think is O(1) is actually O(N)"

I'm fairly certain the developer in question does not understand / care what big-O notation means. Therefore, this almost certainly is not the thought process that lead to the slow load times in GTA. More likely the developer was just mashing things together until "it works".

Bubble sort provides O(n^2) time complexity for a task that can be easily solved with O(nlogn) time complexity. The 5-minute load time for GTA was caused by a developer using an algorithm with O(n^2) time complexity when they could have used an algorithm with O(nlogn) time complexity. So, yes, I would say it actually was the reason (unless you are specifically asking if the bad algorithm choice was bubble sort, in which case the answer is no).
https://nee.lv/2021/02/28/How-I-cut-GTA-Online-loading-times...

tl:dr; O(n^2) string parsing and O(n^2) duplicate removal

> When competitive programming hits the tier where you need to memorize all of the advanced data structures and algorithms to do better, that's where further improvement in the leaderboards no longer translates to better programmers IMHO.

I found this post [1] by the current rank 5 competitive programmer on codeforces to be an interesting take on this perspective. It's not as much about knowing the fancy algorithms, as being able to derive their ideas when needed, that makes you really move up.

[1] https://codeforces.com/blog/entry/92248

> However, the apparent point of competitive programming--building up a good repertoire of advanced algorithms and data structures--is pretty close to useless.

That's not the point of competitive programming. If you talk to anybody who is good at competitive programming, they will tell you it's _not_ about memorizing algorithms and datastructures. It's about developing algorithmic problem solving skills; skills that you can leverage to design (create) algorithms, in order to solve problems.

I have seen successful competitors people do basically that. And I have seen them train how to write basic algorithms super fast again and again.

Yes, they had also algorithmic problem solving skills. But they learned huge amount of algorithms and datastructures so that they don't have to derive them each time. And they spend time literally training how to write them fast without mistake.

It was not just about problem solving skills. It was a lot about knowledge base.

There's not _that_ many algorithms or data structures you see in competitive programming, and the vast majority of them aren't advanced. You do need to memorize a good portion of them, but doing so is the easy part towards becoming good at it.

You can read one moderate length book and know all of the DSes and algorithms you'll need for 99.9% of the time. cses.fi/book is a good one with a free version if you're curious.

https://github.com/kth-competitive-programming/kactl may also be of interest, it contains a good amount of the algorithms/DSes you'd ever need on a few printable pages (20ish).

Ok. I'll take your anecdote at face value and shift my views slightly in that direction. I'll also note that even if some competitors do memorize algorithms & train how to write them fast without mistakes, this memorization perhaps gives them a slight edge, but it's not the core skill that separates winners from losers. A small edge, perhaps.
Necessary edge. There was tons of preparation specifically for competitions.

Not sure why would that be surprising. Same thing happen in chess, scrabble, anything competitive. Once there is enough competition, you can't go in with hope and talent alone. You have to train specifically for competition format.

Chess players don't base their play on pure problem solving either. They memorize and train specific plays.

> Not sure why would that be surprising.

It's surprising because I also do competitive programming, and I know many people who do it, and I haven't encountered anyone in those circles memorizing algorithms. In fact, many competitions explicitly allow you to use a pre-written library. Some IRL competitions even allow you to bring in written materials. (Not all competitions allow this, I know.)

The worst part of competitive programming is that you need to do it for job interviews. This turned a legitimately fun and deeply engaging activity into a truly tedious and draining one
I’d love a programming competition that wasn’t about speed of implementing algorithms, but rather was about speed of implementing solutions, using a provided standard library of algorithms and data structures. Competitive glue-code programming, per se, but glue code that still requires that you understand the need for specific advanced algorithms in order to pull them out of a toolbox.
Isn't this basically what competitive programming is? Maybe I'm a bit clueless, but my assumption was most people have standard libraries of common algorithms/implementations they pull from and tweak/glue as needed to fit the problem in front of them. Those libraries are not standardized, but there is for sure basically a common set of things you need to be competitive, right?
I haven't done competitive programming in years, but when I did we were allowed to use things like Boost in C++ or the standard library in Python.

I can't really recall any scenarios where a stdlib datastructure was slightly too slow, but re-implementing your own version was just fast enough to get by. The problems were typically created in a way where using any common algorithm was 1000x too slow, and you needed something like dynamic programming to make things run in a reasonable amount of time. Or they were a math problem masquerading as a programming problem.

There is a common set of algorithms that most people who do well in such a contest can implement whenever they like, but most of the effort involved in solving the problems happens before they write any code, and many (half or more?) problems can't be solved by just applying the techniques from this repertoire.
This is correct. A typical CodeForces problem is intentionally constructed as a unique snowflake that can't be solved by just calling a library function. You need to be able to design an algorithm that solves that specific unique snowflake on the spot. Perhaps your algorithm also makes some library calls to other algorithms in your comp.coding library, but those are merely building blocks for your own thing, which you have to invent.
I believe the author will agree with your assessment.

> Competitive programming is a good tool for building the programming muscle. An extreme pursuit of competitive programming is worse than useless.

> Almost everything you will ever need is already implemented in a library for you to reuse, and even reaching for that library is pretty rare.

This undercuts your claim a bit as someone has to be able to write the library in the first place. Yes it’s rare, but the software engineering industry is big enough that “rare” can be a “this happens all the time in domain X”.

Overall though I’d agree that it’s not useful for the majority of engineers and isn’t a good judge of programming ability just like math competitions aren’t either. They are “something” though.

Writing foundational algorithms/data-structures libraries (e.g. language-runtime stdlibs like libc) is usually treated as a sort of sacred ritual in programming—something where it’s “obvious” that it should be done slowly, carefully, and without distraction, with each change audited carefully by many peers before accepting a merge.

It’s not often that you’re writing such code “in anger”, under time-pressure to ship to fix downtime, with no time to get peer review.

This is in large part because the downstream client of an algorithms/data structures library, would almost-always prefer a robust but less-efficient option, over an alpha-quality implementation of a more-efficient option. Dataset-size scaling challenges that recommend algorithmic efficiency increases can, almost always, be temporarily put off by “throwing machines at the problem” until the more-efficient library becomes high-quality enough to be fully relied upon. Until then, the business logic will make do with the simpler/more naive algorithm.

And in most language runtimes, there’s a simpler/more naive algorithm or data structure to solve every problem already laying around in the core stdlib; so it’s not like you, as the library writer, have to implement that code “in anger” either. Your internal customers can upgrade from depending on stdlib types, to depending on your lib’s types, when using your lib becomes an unalloyed win for them; and until then, do nothing.

> Writing foundational algorithms/data-structures libraries (e.g. language-runtime stdlibs like libc) is usually treated as a sort of sacred ritual in programming—something where it’s “obvious” that it should be done slowly, carefully, and without distraction, with each change audited carefully by many peers before accepting a merge.

I'll be honest, I think you're vastly overestimating how well these things are actually developed :) I agree with what you said, I just found this part a little bit funny.

What I really meant, without the fancy language, is that stdlib programmers are crotchety greybeards who don't like change-for-change-sake and so default-deny rather than default-accept random patches. That by itself creates a very different level of stability in these projects, where things only ever change if they really need to to fix a specific bug—and even then, the change is pared down to have as little architectural effect as possible.
You most likely need strong grasp over OO, and design patterns to be a good library designer/programmer than Algorithms that you can easily look up online.