What are some hidden red flags to look for when doing code review?
- Page Owner: Not Set
- Last Reviewed: 2019-11-05
When reviewing another developers code, what are some red flags I should look for?
Answers requested for
- Using Httpclient wrong
- SiteDefinition.current.StartPage in a scheduled job.
Additional Posts
A few specific things:
- If using
HttpClient, do notDisposeof and recreate an instance for each request. Instead, create on instance and cache that instance for the entire application. See here: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/ . - Inside a Scheduled Job, you can't rely on things that rely on
HttpContext, because there is no context (Scheduled Jobs run in a separate thread generally). So things likeSiteDefinition.Currentmight not return what you expect (especially if none of the sites in Episerver are set up as the*fallback site). For site definitions, useISiteDefinitionRepositoryinstead. - If you're using
FindPagesByCriterain any place that gets hit frequently (such as a normal Page Controller), you almost certainly should be caching.FindPagesByCriteriais slow and doesn't have built-in caching likeIContentLoadermethods do. - If you're using
ContentLocator, you might need caching. Many of those methods either useFindPagesByCriteriaor load a significant amount of content to search. Consider using an external index such as Find, Solr, or Content Index. - Transient data should not be put on
IContentobjects. If you need to store info for a page view, put it in the View Model class, not the content object. Content objects share a single instance across threads so any transient data you put on the object gets shared across all references to that object. This is not the case for View Model classes, where you can and should store transient data. - Never use
CreateWritableClone()unless you're planning to save changes to some content. If you find yourself needing to call this method for any other reason, you're doing something wrong. DDSis great for storing small amounts of data. It quickly becomes slow and cumbersome for large datasets.- Don't get
SiteDefinitionvia dependency injection. UseSiteDefinition.Currentif you have a request, orISiteDefinitionRepositoryif you don't. Maybe it's been fixed, but in our experience, the injected SiteDefinition would always return the fallback site, not necessarily the one the user is currently on. - If you're putting something in a query string, use some kind of builder or make sure to URL encode the value.
/search?q=@searchis bad.search?q=@Uri.EscapeDataString(search)is better. - I always look at
@Html.Rawextra closely - serious potential for XSS vulnerabilities. Use@Html.PropertyForor@Html.XhtmlStringforXHtmlStringvalues (otherwise visitor groups and embedded blocks don't render correctly). - Forms that have an effect (such as signing up for an email list, or changing data somewhere) must have a validated antiforgery token and be a
POST. Forms that don't have an effect (such as search forms) don't need a token and should be aGET. - If you're using
GetChildren<T>for navigation, you should be filtering the results for access level and publish dates.
Some general things:
- Avoid putting logic or "work" in constructors. Constructors should be quick and should basically never throw exceptions. If you're object requires work to be created, if possible, consider putting that work in a
staticmethod and making the constructor private. Something likeMyObject.CreateInstance(...args). - If you catch an exception, you need to log it. Otherwise, you might as well write
catch(Exception) { /* Any problems here will go unreported for a long time, and when we finally hear about it we'll have no useful information! */ }In general, do not catch exceptions unless you can gracefully handle the failure or if it's truly an exception you don't care about (such as a Twitter feed failing to load). Globally, uncaught exceptions should be handled and sent to Sentry. - Getters and Setters should be lightweight and have no external dependencies. Getters should have no side effects. If your getter or setter is hitting a database, it should be a method. If your getter changes a value somewhere else, it's probably wrong. In general, Properties are for fast, simple logic. I should be able to repeatedly call a property without worrying about it being slow or affecting other things.
- Don't repeat yourself (within reason). If you've got a long LINQ query for example, cache the result of that query in a variable instead of copy/pasting it multiple times. Assign chunks of logic to variables or functions and give it a good name. Much easier to read
if (userIsAllowedToEdit)in 3 places thanif (user.IsActive && (!user.CanExpire || !user.IsExpired) && (user.IsInRole("Admin") || (user.IsInRole("Editor") && user.Name == page.Owner))repeated the same number of times.