Avoid Under-Engineering
It's usually best to not simply solve a problem, but to solve it well. Expect the unexpected, such as invalid input or missing resources.
Build software with future maintenance and extensibility (feature additions) in mind. On a larger scale this often involves modularity. Code written with shorthand "tricks" is harder for others to read and difficult to maintain. While no one knows exactly what features users will ask for down the line, write applications as future proof as possible.
Avoid copy-and-paste programming. If a set of logic or particular block of code is to be used in more than one place, place it into a function, object, or library and call that one common routine from multiple places. Copying and pasting anything but the simplest of code is lazy and leads to more difficult bug fixing and software maintenance. If the logic of duplicated code needs to change, each copy must be found and altered. This isn't necessary if properly modularized.
 Avoid Over-Engineering
Excessively simplified code can miss potential problems, but excessively complicated code can create more issues. Code that is over-engineered is more difficult to maintain, can introduce more bugs, and typically reduces readability. Consider how a new member of a development team would adapt to such a system. Too much attention to detail can also waste time during initial development. The goal is to strike a balance between elegant solutions and developer efficiency.
Delegate work to other applications, libraries, and frameworks when possible. Don't "reinvent the wheel." For common features which many others have tackled before, it's very likely that someone else has already solved the problem with a very thorough and complete solution.
But delegating too much will lead to a loss of control and understanding. Be especially careful of "black boxes," where all of the details are hidden away.
Consider how others will delegate to your software, as well. If your application is that "black box" then others will have certain expectation and assumptions.
Secondary systems may not always be available. For example, a network connection or web service may disappear, and that fact should not hang your application or keep the end user waiting for too long.
Conversely, not trusting end users enough leads to frustration. This frustration will lead some to work for ways around the bottlenecks your software has created. This, ironically, can lead to even more security issues.
Also assume that your software will not be completely trusted by other systems or people. Provide ways to security access your features.
 Code Quality
Code readability is important for teamwork and software maintenance. Follow good coding standards and avoid code bloat. Perform regular code reviews to make sure these best practices are being adhered to by everyone on the team.
Focus on optimization as needed. Premature optimization can lead to over-thought and over-engineered code. It's typically best to build a logical, well architected system first, then analyze for bottlenecks and focus on effective optimization.
Store code in a revision control system to track changes and manage code branches. These systems can also be used by lead developers to monitor all changes before they end up being merged into the final codebase.
Documentation is an important part of building quality software. Start by gathering complete software requirements and write detailed software specifications. These steps guide development toward successful goals and make sure the solution is properly planned before development begins.
During and after software development keep track of issues with detailed bug reports. This is a project management task that's required for developer workflow and accountability.
 See Also
Also consider the best practices for specific systems and situations: