Couple of months back I completed my first year at Enthought. Before that I have worked twice in Google Summer of Code for Drupal. There are quite a few things I learnt while working on various projects:
Tests are just as much important as writing code
Tests, which are often treated as a second class citizen of the project are actually an important part of the project. They are immensely helpful in checking if the code is working as expected and that any particular change in code broke a previously working part of the codebase. They are also useful in checking if the system is built as per the requirement specifications. After having few bad experiences and wasting a lot of time fixing parts which didn't have tests, I now use Test-driven development for most of my new projects/modules. They will save a lot of time and effort in the long term.
Premature optimization is the root of all evil
The most important thing in software engineering is delivery of code. However, a working unoptimized software is better than an optimized non-working code, assuming that the working implementation is usable till some extent. "Implement first, refactor and optimize later". This is helpful because if you already have working code you can write tests against it and later when you refactor you can run the same test suite against the refactored code and check if it is working as expected or not. Also you can profile your code and check if that the refactor was useful or not.
Refactor is inevitable
The heading says all. No matter how much thought you have put into the designing or architecting the new project/module, your code will need refactor, its just unavoidable. "Always be ready to throw one away."
Additional reading: Case 105 Navigation
Document your code and project
When working in a team, remember that your code is going to be read, used, modified by other team members. Try to keep your code as readable, clear and documented as possible so that its easy for others to use it.
When developing libraries/APIs, build documentation! that makes it easier for others to look at your project and the various APIs it exposes without going through your source code and figuring out how different modules connect and interact with eachother. Add example code and common use cases to the documentation. Usage of your library/project can depends highly on how well it is documented.
Debugging is nice way to learn new things
I learnt a lot of things from debugging. Commonly, bugs are caused because we didn't think of certain edge cases or the platform behaves in the slightly different manner or the environment has a different configuration. Debugging and discovering such bugs makes you think of such cases when you write similar type of code in the future and consider those scenarios while developing. Sometimes, debugging can also help you realize how stupid you can be (sometimes) :P
You also discover the quirks of different projects/libraries and how they differ from each other.
For example, in PostgreSQL by default UTF8 VARCHAR column values are case sensitive, but in MySQL UTF8 VARCHAR column values are by default case insensitive, to use case sensitive values, the collation has to be UTF8_CS.
Eighty Twenty Rule - "80% of the code takes 20% of your time, and the other 20% will take 80% of your time."
Believe it or not, developers are very bad at time estimation. The estimated time to complete and the actual time taken are usually very different. I personally quadruple the estimated time for small estimations and approximately double for large ones. For example, if I estimate time required as 15mins then I will quote an hour, or if I estimate as 3 days, I will quote 4-5days.
There is no good architecture, its all tradeoffs
"Design for the present with future in mind."
Solve the immediate problems at hand, but keep the future use cases in mind while solving them. Your system architecture will mainly depend on the requirements, but requirements change over the period of time, so make sure your design is flexible till some extent. Completely generic solutions often add complexity to the code. So limit the amount of flexibility and modularity depending upon your experience and requirements, there are lot of other factors to this though.
The Teddy Bear problem solving method works!
If you get stuck on a problem and you know you can solve it yet the solution seems elusive. Use the The Teddy Bear Problem Solving Method.
"Pick up a teddy bear and explain the problem to the teddy bear. 9 times out of 10 you'll be able to solve the problem."
Delivering software is not enough, support is also important
If people are going to use your software, they will have problems and they will have questions. Its very important to establish a communication channel that the users can use to contact you and ask those questions or provide feedback/suggestions, explain the specific use case they might be interested in. Having good documentation is very helpful in reducing the support overhead.
Take licensing seriously
When writing commercial code, always consider the licensing of libraries you are using. This is very important from legal point of view. Also when you are releasing your code, use appropriate licenses.
Everything you do will add to your experience
Every wrong decision you make, mistake you commit will add to your experience (just try not to repeat them :-)) and so will all the correct decisions you make. "Whatever you do, you are going to learn something out of it :)"
I think have covered most of the points and will keep updating this post as and when I get time.
P.S.: original answer here on Quora here.