Modern programming languages provide many advanced features that many developers will not be familiar with. Often this is because the features are not applicable to a problem domain or the developer uses an alternate approach to problems addressed by the feature. This is rarely a criticism, there are no prizes in production code for how many advanced features you're taking advantage of. Use of advanced features should be governed by whether they make the code more maintainable, extensible, flexible, testable and correct. If you are not getting these benefits then you are unlikely to be using them legitimately and should consider alternative approaches.
A common concern not listed above is whether the feature may be understood by the development team. There is an argument that advanced features are complex and difficult to understand and that therefore they should be avoided because other developers may not be able to understand them. I call this the my coworkers are idiots argument and I don't accept it for a number of reasons:
- Coding down to your least able developer restricts the implementation to fit within the capabilities of that developer.
- Coding down to your least able developer prevents them from extending their skills and capabilities.
- It's entirely legitimate to expect that a professional developer become familiar with the full range of capabilities of the languages they use when given adequate time and support. If they are not capable then (harsh though it is) the appropriate response is to replace them for being unable to perform the job for which they are employed.
- Many of your coworkers are not in fact idiots, difficult though it can sometimes be to see this through your own ego.
Sophisticated code, that which uses advanced features to implement functionality more effectively, should therefore be encouraged. This requires that the decisions to use the advanced features be well thought out and rational, and will also require that the code be readable and supported by adequate comments and documentation to allow all team members to work with it effectively.
The contrast is complex code. Complex code results when the code to implement a software function demonstrates an overly involved mechanism for producing the desired result. It is common to see complex code apply patterns by rote without understanding or use more levels of indirection that the problem demands. This additional complexity has a cost to the maintainability and robustness of the code and makes it more difficult to test to ensure correctness.
Like sophisticated code, complex code often also uses advanced features. The relevant difference is the justification for their use. Sophisticated code can be shown to have reason to use an advanced feature commensurate. Complex code will not. Generally if you examine why complex code is constructed as it is the reasons will be such things as a developer who thinks the feature is cool or interesting, or who assumes that because it's powerful it's inherently superior. These are not legitimate justifications as they do not address the needs of the software itself.
Often the difference between sophisticated and complex code is a few simple refactorings. In other cases the sophisticated thing to do is implement the functionality in a simple and straightforward manner than can be easily understood and maintained. In yet other cases the difference is context, what may be a sophisticated usage in some places is merely complex in others. Identifying these cases is a matter of experience and the ability to sanity check how you are implementing functionality. While sophisticated code is to be encouraged this must be encouragement of true sophistication and not simply conjoining the concepts of sophistication and complexity.