Secret ingredients to quality software

  • Services
    • Products
      • Training
        • User Group
          • Rules
            • About Us
              • SSW TV
              SSW Foursquare

              Orphaned Rules - 32 Rules


              The rules listed below have no parent category
              1. Do you avoid adding unnecessary CSS classes?

                When making or editing CSS or HTML content it is important to avoid adding classes and ID's unnecessarily.

                It can be tempting to add classes on elements, as it is often the most obvious solution to CSS problems... but doing so will lead to overly cluttered code and a host of overly specific solutions. When working on CSS it is almost always better to reuse rather than adding additional classes.

                You should use a front-end framework, like Bootstrap or Tailwind. The best front-end frameworks will include most of the clases you will need on a project. Basically you will only need new classes for very specific layout elements.

                HTML:

                <a class="view-all-link" href="https://www.youtube.com/playlist?list=PLIzW_0dAIKv3mjBeK8eyJbe1bOGWJX_UV">View All</a>

                CSS:

                .view-all-link{
                  margin-top: 0;
                }

                Figure: Bad example - The "view-all-link" class was added unnecessarily

                HTML:

                <a class="mt-0" href="https://www.youtube.com/playlist?list=PLIzW_0dAIKv3mjBeK8eyJbe1bOGWJX_UV">View All</a>

                Figure: Good example - Using Bootstrap's class "mt-0" has the same affect without adding any class

              2. Do You create a combo-box that has a custom template?

                When designing your form, you should try to help your user whenever it's possible. So it's a good idea to create a combo-box that has a custom template.

                multiplecolumns
                Figure: Good example – Combo-box with custom template

                Feel free to use our sample:

                1. Download and install Kendo UI Controls from Kendo UI
                2. HTML (Razor) - Create a combo-box that has a custom template. Use a code bellow as an example:
                @(Html.Kendo().ComboBoxFor(x => x.EmpTime.ProjectID)
                .AutoBind(true)
                .Suggest(true)
                .Delay(300)
                .DataSource(source => source.Read(read => read.Action("ProjectNameAjaxBind", "Ajax")
                .Data("function() { return {clientId : getClientId()}; }")
                .Type(HttpVerbs.Post)))
                .Height(450)
                .DataTextField("DisplayText")
                .DataValueField("Value")
                .Filter(FilterType.Contains)
                .Template(@"
                <table class='comboBox-Projects'>
                <tr>
                <td class='projectName'>${data.DisplayText}</td>
                <td class='projectTotalCount'>${data.UsedCount}</td>
                <td class='projectLastUsed'>${data.LastUsedText}</td>
                </tr>
                </table>
                ")
                .HighlightFirst(false)
                .IgnoreCase(true)
                .Events(e => e.Change("projectChanged").Open("onProjectOpened"))
                )
                1. CSS - Customize the look & feel to suit your needs. Example:
                #projectsTableBorder {
                 border-bottom: 1px solid rgb(217, 217, 217);
                 margin: 0 -2px;
                 }
                 .comboBox-Projects#projectsHeader {
                 color: black;
                 font-weight: bold;
                 margin: 4px 16px 4px 4px;
                 }
                 .comboBox-Projects td.projectName {
                 width: 400px;
                 text-align: left;
                 }
                 .comboBox-Projects td.projectTotalCount {
                 width: 70px;
                 text-align: right;
                 padding-right: 16px;
                 }
                 .comboBox-Projects td.projectLastUsed { text-align: left; }
                 #projectNameHeader:hover, #projectTotalCountHeader:hover, #projectLastUsedHeader:hover {
                 cursor: pointer;
                 text-decoration: underline;
                 }
                1. JavaScript - Use JavaScript to change the combo-box's behavior. Example:
                // resize the drop-down list
                function resizeComboBoxList(comboBoxListId, width) {
                 var list = $(comboBoxListId);
                 list.width(width);
                 var height = list.height();
                 list.children("ul").height(height - 25);
                }
                function onProjectOpened() {
                 resizeComboBoxList("#EmpTime_ProjectID-list", 600);
                }
                // execute sorting when a header column is clicked
                function onClick_ColumnHeader(senderId, comboBoxId, fieldName) {
                 var column = $(senderId);
                 column.unbind('click');
                 column.click(function() {
                 sortComboBoxBy(comboBoxId, fieldName);
                 });
                }
                // sort any combo-box based on a field name
                function sortComboBoxBy(comboBoxId, fieldName) {
                 var comboBox = $(comboBoxId).data("kendoComboBox");
                 var sortDescriptors = comboBox.dataSource._sort;
                 var direction = "asc";
                 if (typeof(sortDescriptors) != "undefined") {
                 var sortDescriptor = sortDescriptors[0];
                 if (typeof(sortDescriptor) != "undefined") {
                 if (sortDescriptor["field"] == fieldName) {
                 if (sortDescriptor["dir"] == "asc") {
                 direction = "desc";
                 }
                 }
                 }
                 }
                 comboBox.dataSource.sort({
                 field: fieldName,
                 dir: direction,
                 });
                }
                // prepare 
                $(document).ready(function() {
                 var projectsId = "#EmpTime_ProjectID";
                 var projectsListId = projectsId + '-list';
                 // prepend header to combo-box list. By default you only get <ul>
                 $("
                 
                Project Name</td>" +
                 "<td id='projectTotalCountHeader' class='projectTotalCount'>Used</td>" +
                 "<td id='projectLastUsedHeader' class='projectLastUsed'>Last Used</td>" +
                 "</tr></table>" +
                 "</div>")
                 .prependTo(projectsListId);
                 // register click events for each column
                 onClick_ColumnHeader('#projectNameHeader', projectsId, "DisplayText");
                 onClick_ColumnHeader('#projectTotalCountHeader', projectsId, "UsedCount");
                 onClick_ColumnHeader('#projectLastUsedHeader', projectsId, "LastUsedValue");
                });
                });
              3. Do you know how to make lost opportunities more visible?

                Losing opportunities is very common for a business. Keeping track of the win/lost ratio is a good start if you want to track how you're doing, but making sure you keep a close look on what you've lost more important why, is critical.

                The basics

                There are a number of reasons you may lose an opportunity: The project may be cancelled, you may be too expensive or out-sold, or maybe you simply don't have the capacity to cater for bookings.

                If you want to report on lost opportunities, the first step is to make sure Sales have the opportunity to do so. Make sure you provide a field with enough options when closing an opportunity:

                lostoppobad
                Figure: Only giving a couple options is not enough

                lostoppobad
                Figure: Give at least 4 options

                Once the information is stored in CRM, you can then easily report on it, e.g. using PowerBI or any other reporting tool. This provides great insights to make educated business decisions (i.e. "should we hire more?")

                Going the extra mile

                Reporting is great, but this may come back through your feedback loop a bit too late. If you want to be alerted when you just lost a big opportunity because you don't have enough staff, you can go the extra mile and setup alerting based on conditions.

                The best way to do so is to setup a LogicApp to trigger when an opportunity has been lost, setup your filters (e.g. Budget Amount >= $100,000 and Reason = "Not enough staff") and you're done!

              4. Do you disable connections for TFS 2010 migration?

                Once you are ready to start you need to make sure that no one can access the existing TFS 2008 server while you do the migration.

                1. You are ready to start
                2. Send out an email notifying all users that TFS2008 will be turned off. Follow Do you have a server reboot/restart policy?
                3. Make sure no-one can check in files by either: A. Running TFSQuiesce (recommended)

                  B. Turning off TFS Service 1. Remote desktop into TFS 2008 2. Start IIS 3. Right click Team Foundation Server | Stop

                StopTFSServices
                Figure: You need to stop anyone checking in files

                1. Confirm you can no longer get latest on the team project
              5. Do you disable connections for TFS 2015 migration?

                It is important that while you're upgrading, nobody can check in. Any check-ins after you backup your database will be lost.

                To make sure that nobody can change anything during the upgrade, follow these steps.

                a. Send out an email notifying everyone TFS will be unavailable for the upgrade period

                b. Make sure nobody can check in files:

                c. Open the TFS Administration Console on the server.

                d. Navigate to Application Tier / Team Project Collections.

                e. For each Team Project Collection, select it, and click "Stop Collection". Enter a useful message (this will be displayed to users trying to connect from Visual Studio) and click "Stop":

                stop each term
                Figure: Stop each Team Project Collection

                all team project
                Figure: All Team Project Collections are stopped

                f. In Visual Studio, confirm you can no longer connect to TFS

                visual studio
                Figure: Visual Studio shows the message that you entered when you stopped the Team Project Collection

              6. Do you get rid of legacy items in SharePoint Online?

                Most people have now moved to SharePoint Online, either from scratch or by migrating content from previous versions of SharePoint. In SharePoint Online, a lot of legacy features have been deprecated and replaced by more modern counterparts. Unfortunately, chances are you have ported a whole lot of useless things as part of your migration.

                [TODO: Screenshot of Useless stuff]

                Before removing seemingly useless or old stuff, you should always make sure that site owners are OK with the deletion and that the data is actually not useful. In most cases however, the libraries detailed below have been added to your site by default and contain only demo and/or defautl data.

                Process

                The process to remove legacy items is always the same regardless of the library/list you're trying to remove:

                1. Find the product or feature that replaces it in SharePoint Online
                2. Deactivate the site feature associated with the list or library:

                Either manually via:

                Site Settings | Site Features | Feature XYZ | Deactivate

                Or programmatically using:

                # Connect and get context
                Connect-PnPOnline -Url https://contoso.sharepoint.com
                
                # Get Feature Object
                $Feature = Get-PnPFeature -Scope Web -Identity $FeatureId #Feature IDs below
                 
                # Get the Feature status
                If($Feature.DefinitionId -ne $null)
                {    
                    # De-activate the Feature
                    Disable-PnPFeature -Scope Web -Identity $FeatureId -Force
                }
                Else
                {
                    Write-host -f Yellow "Feature is not active!"
                }
                1. If feature is already disabled or if SharePoint throws an error on deactivation (can happen for very old features), perform a forced delete of the library using powershell.
                # Connect and get context
                Connect-PnPOnline -Url https://contoso.sharepoint.com
                $ctx = Get-PnPContext
                
                # Get list object
                $list = $ctx.Web.Lists.GetByTitle("Library_To_delete")
                
                # Force delete (send to recycle bin)
                $list.AllowDeletion = $True
                $list.Update()
                $ctx.ExecuteQuery()
                $list.Recycle() | Out-Null 
                $ctx.ExecuteQuery()

                MicroFeed

                Microfeed has been replaced in SharePoint by Yammer webparts and is no longer supported.

                Associated Site feature to disable: "Site Feed" GUID: 15a572c6-e545-4d32-897a-bab6f5846e18

                Announcements

                Annoucements are now replaced by the news webparts [TODO: Link to news webpart doc]

                Associated Site feature to disable: "Announcement Tiles" GUID: 00bfea71-d1ce-42de-9c63-a44004ce0104

                Workflow Tasks

                SharePoint Worklow are deprecated and replaced by Power Automate [TODO:link to rule to PowerAutomate]

                Associated Site feature to disable: "Workflow Task" GUID: 57311b7a-9afd-4ff0-866e-9393ad6647b1

                Drop-Off Library

                Drop-Off Library is not deprecated in SharePoint Online explicitely, however it is considered best practice to user Power Automate to achieve the same results with much greater flexibility and maintainability.

                Associated Site feature to disable: "Content Organizer" GUID: 7ad5272a-2694-4349-953e-ea5ef290e97c

              7. Do you know what currency to quote overseas?

                The easiest thing to do is to quote the price in your local currency. Go the extra step and convert it to a currency they understand.

                If you are not sure what currency to use, just use US Dollars, which is the international currency.

                Of course, if you are dealing with somebody regularly, then you should know their preferred currency.

                E.g. Chinese company billing someone in Brazil:

                Our price is CNY ¥1,000

                Figure: Bad example - the client will need to convert it

                Our price is CNY ¥1,000 (about USD $160)

                Figure: Good example - USD gives a clear understanding of the costs

              8. Do you know the difference between a 'smart' and a 'clever' developer?

                When we first start out as a developer, we want to show the world what we can do by creating complex solutions. As we gain experience, we learn to show our worth by creating simple solutions.

                Developers are like a fine wine, they get better with age.

                Lets take this piece of code as an example:

                <span className="text-xl">
                    {
                        targetedDays === 0 ? "Today" : 
                        targetedDays === -1 ? "Yesterday" : 
                        targetedDays === 1 ? "Tomorrow" : 
                        moment().add(targetedDays, 'days').format("dddd D MMMM YYYY")
                    }
                </span>

                One liner! Nailed it 🥳 Pity the next developer who has to decipher what is going on! The cognitive load here is really high! and the the maintainability, is really low. What is the first thing you are going to need to do if this piece of code start behaving poorly?

                Now lets take the following reformatted code example:

                const getTargetedDayAsText = (targetedDays) => {
                  if (targetedDays === -1) {
                    return "Yesterday";
                  } elseif (targetedDays === 0) {
                    return "Today";
                  } elseif (targetedDays === 1) {
                    return "Tomorrow";
                  } else {
                    let days = moment().add(targetedDays, 'days');
                    let formatted = days.format("dddd D MMMM YYYY");
                    return formatted;
                  }
                }
                
                <span className="text-xl">
                    {getTargetedDayAsText(targetedDays)}
                </span>

                Now this is nowhere near as terse, but anyone looking at it is able to quickly determine what is going on. And anyone who has to investigate any issue with the code is going to be able to step through and debug this without any issues.

                This above is not an overly complicated example but now imagine something like this example

                var pixelsQuery =
                    from y in Enumerable.Range(0, screenHeight)
                    let recenterY = -(y - (screenHeight / 2.0)) / (2.0 * screenHeight)
                    select from x in Enumerable.Range(0, screenWidth)
                           let recenterX = (x - (screenWidth / 2.0)) / (2.0 * screenWidth)
                           let point =
                               Vector.Norm(Vector.Plus(scene.Camera.Forward,
                                                       Vector.Plus(Vector.Times(recenterX, scene.Camera.Right),
                                                                   Vector.Times(recenterY, scene.Camera.Up))))
                           let ray = new Ray() { Start = scene.Camera.Pos, Dir = point }
                           let computeTraceRay = (Func<Func<TraceRayArgs, Color>, Func<TraceRayArgs, Color>>)
                            (f => traceRayArgs =>
                             (from isect in
                                  from thing in traceRayArgs.Scene.Things
                                  select thing.Intersect(traceRayArgs.Ray)
                              where isect != null
                              orderby isect.Dist
                              let d = isect.Ray.Dir
                              let pos = Vector.Plus(Vector.Times(isect.Dist, isect.Ray.Dir), isect.Ray.Start)
                              let normal = isect.Thing.Normal(pos)
                              let reflectDir = Vector.Minus(d, Vector.Times(2 * Vector.Dot(normal, d), normal))
                              let naturalColors =
                                  from light in traceRayArgs.Scene.Lights
                                  let ldis = Vector.Minus(light.Pos, pos)
                                  let livec = Vector.Norm(ldis)
                                  let testRay = new Ray() { Start = pos, Dir = livec }
                                  let testIsects = from inter in
                                                       from thing in traceRayArgs.Scene.Things
                                                       select thing.Intersect(testRay)
                                                   where inter != null
                                                   orderby inter.Dist
                                                   select inter
                                  let testIsect = testIsects.FirstOrDefault()
                                  let neatIsect = testIsect == null ? 0 : testIsect.Dist
                                  let isInShadow = !((neatIsect > Vector.Mag(ldis)) || (neatIsect == 0))
                                  where !isInShadow
                                  let illum = Vector.Dot(livec, normal)
                                  let lcolor = illum > 0 ? Color.Times(illum, light.Color) : Color.Make(0, 0, 0)
                                  let specular = Vector.Dot(livec, Vector.Norm(reflectDir))
                                  let scolor = specular > 0
                                                 ? Color.Times(Math.Pow(specular, isect.Thing.Surface.Roughness),
                                                               light.Color)
                                                 : Color.Make(0, 0, 0)
                                  select Color.Plus(Color.Times(isect.Thing.Surface.Diffuse(pos), lcolor),
                                                    Color.Times(isect.Thing.Surface.Specular(pos), scolor))
                              let reflectPos = Vector.Plus(pos, Vector.Times(.001, reflectDir))
                              let reflectColor = traceRayArgs.Depth >= MaxDepth
                                                  ? Color.Make(.5, .5, .5)
                                                  : Color.Times(isect.Thing.Surface.Reflect(reflectPos),
                                                                f(new TraceRayArgs(new Ray()
                                                                {
                                                                    Start = reflectPos,
                                                                    Dir = reflectDir
                                                                },
                                                                                   traceRayArgs.Scene,
                                                                                   traceRayArgs.Depth + 1)))
                              select naturalColors.Aggregate(reflectColor,
                                                             (color, natColor) => Color.Plus(color, natColor))
                             ).DefaultIfEmpty(Color.Background).First())
                           let traceRay = Y(computeTraceRay)
                           select new { X = x, Y = y, Color = traceRay(new TraceRayArgs(ray, scene, 0)) };
                
                foreach (var row in pixelsQuery)
                    foreach (var pixel in row)
                        setPixel(pixel.X, pixel.Y, pixel.Color.ToDrawingColor());

                This was fortunately just someone's exercise in proving that they could and happily states

                Just because you can, doesn't mean you should!

                So what are some of the things that developers learn over time that takes them from being a Clever developer to being a Smart developer?

                Avoiding problems

                Clever developers fix a problem where Smart Developers stop a problem from happening.

                Lets say you receive a PBI saying that XYZ method is always returning a value 0.001 more than it should.

                Smart Developer

                Identifies that some incoming data is always out and results in the small rounding issue.

                return (value-0.001) 

                Clever Developer

                Identifies that a method downstream is rounding to 2 decimal places and removes this.

                Understanding the whole before they start

                Code costs money, not just to create but also to maintain.

                Figure: Clever developer coding away and resolving PBI's

              9. Do you know which check-in policies to enable?

                Check-in policies are a great tool to enforce quality code before it hits your source control repository. SSW recommends that the following check-in policies be enabled by default on your project:

                1. Changeset Comments Policy - To enforce that all check-in contain comments
                2. SSW Code Auditor - To enforce coding standards and best practices before check-in
                3. Testing Policy - To enforce that unit tests should all pass before code can be checked-in
                4. Code Analysis Policy – To enforce that code analysis rules are not broken
                5. Builds Policy – To enforce that the developer has built the project end to end before they check-in

                More Information

                To enable these policies:

                1. Right click the Team Project in Team Explorer > Team Project Settings > Source Control
                2. Select the check-in policies above
                3. Click OK

                SC TFSCI
                Chose check in policy
                Figure: Chose check-in policies in TFS

              10. Do you look for call back over event handlers?

                [bad code]

                [good code]

              11. Do you look for memory leaks?

                a. These could be in the UI and middle level tiers

                b. There are common patterns for memory leak, e.g. proxy to WCF [code]

                e.g RIA service [code]

                e.g. Load +=     (9 out of 10 people will forget to remove their statement)

                Google: Common Memory Leak
                [bad code]

                [good code]

              12. Do you read “Timeless way of building” ( has relevance to software)?

                This field should not be null (Remove me when you edit this field).

              13. Do you review the Builds?

                Quote: don’t use gated check-ins because…. thanks Peter Provost

              14. Do you use part of Sprint review to drill into the next most important number in this list?

                a. Use the retro to come up with better work practises

                b. Image of our “Scrum 8 steps” 

                Google: Tips for Retrospective

              15. Do you use the Web API (REST)?

                RIA services – RIP

                WCF – RIP(Now shipped in Asp.Net MVC4)

                SOAP - RIP

              16. Do you look for native code that’s missing dispose?

                a. Suggestion to MS: make a tool 

              17. LinkedIn - Do you know the best way to announce you got a new job or certification?

                Did you know that making a splash on LinkedIn when announcing your new job can enhance your professional network and career prospects?

                With over 740 million users globally and 55 million registered companies, LinkedIn has become the go-to platform for professional connections and opportunities. In fact, individuals who announce their new positions on LinkedIn can receive an average of 10 times more profile views and higher engagement from recruiters and industry peers.

                Adding an image to your new job announcement post catches the viewer's attention and adds visual appeal. Images break up the text-heavy nature of the platform, increasing the likelihood that users will stop scrolling and engage with your post.

                After you add a new job, LinkedIn suggests announcing this to your network and offers a few templates on their platform that aren't very eye-catching.

                We suggest you add a branded image or a nice photo of your first day!

                The same principle applies to sharing new certifications or courses. You can add an image to your post to make it an engaging announcement!

                linkedin new job template
                Figure: Bad example - LinkedIn offers templates without any personality, which most users will just keep scrolling and not really stop to read as they are very common

                linkedin new job branded
                Figure: Good example - The branded image made a difference in the post and had a much higher engagement 🚀

              18. Do you know when to use an on-premises build server with Azure DevOps?

                If you are using Azure DevOps, there's an awesome Hosted Build Server option you can use to perform your builds with little to no setup. But if you're working on a project that has continuous integration and a long build time, it's not long before those build minutes start to add up.

                To reduce your build costs, you could reduce the number of builds or try to reduce the build time. Continuous integration is very important so you don't want to turn that off, and we encourage checking in early and often. Reducing the build time is a nice idea, but you can't always reduce it enough to make a difference.

                For large, expensive projects, the best option is to configure an on-premises build server rather than using hosted builds.

                To configure an on-premises build server for Azure DevOps, check out Anthony Borton's great walkthrough.

                Once you have a build server configured, you'll be able to see the build controller as an option when you configure a new build definition.

                vso build
                Figure: Good Example - We have the option of an on-premises build controller as well as the Hosted Build controller

              19. Do you know the relevant Power Platform certifications and associated exams?

                Whether you're an expert or just getting started, working towards gaining a new certification is a worthwhile investment.

                Microsoft provides numerous certifications and training options to help you:

                • Learn new skills
                • Fill technical knowledge gaps
                • Boost your productivity
                • Prove your competence

                certification map
                Figure: Microsoft Certification RoadMap


                Fundamentals

                If you're just getting started, take a look at:

                Microsoft Certified: Power Platform Fundamentals

                Earn this certification to prove you have a foundational knowledge of the Power Platform.

                You will need to pass Exam PL-900: Microsoft Power Platform Fundamentals.


                Associate

                Once you've mastered the fundamentals, developers should move on to:

                Microsoft Certified: Data Analyst Associate

                Earn this certification to prove you have skills and knowledge in data processing and storage.

                You will need to pass Exam DA-100: Analyzing Data with Microsoft Power BI.

                Microsoft Certified: Power Platform Functional Consultant Associate

                Earn this certification to prove you can build solutions on the Power Platform using low code or no code solutions.

                You will need to pass Exam PL-200: Microsoft Power Platform Functional Consultant.

                Microsoft Certified: Power Platform Developer Associate

                Earn this certification to prove you can design, develop, secure and extend Microsoft Power Platform solutions.

                You will need to pass Exam PL-400: Microsoft Power Platform Developer.

                Microsoft Certified: Power Platform App Maker Associate

                Earn this certification to prove that as a business stakeholder you can engage well with Power Platform developers to design and specify business automations.

                You will need to pass Exam PL-100: Microsoft Power Platform App Maker.

                Microsoft Certified: Dynamics 365 Business Central Functional Consultant Associate

                Earn this certification to prove you have a solid knowledge of Dynamics 365 Business Central.

                You will need to pass Exam MB-800: Microsoft Dynamics 365 Business Central Functional Consultant.


                Specialty

                Microsoft Certified: Customer Data Platform Specialty

                Earn this certification to prove you understand using the Power Platform to manage customer retention, improve customer experiences.

                You will need to pass Exam MB-260: Microsoft Customer Data Platform Specialist.


                Expert

                Microsoft Certified: Power Platform Solution Architect Expert

                Earn this certification to prove you have a foundational knowledge of the Power Platform.

                You will need to pass Exam PL-600: Microsoft Power Platform Solution Architect

                and complete one of

                Microsoft Certified: Power Platform Functional Consultant Associate

                OR

                Microsoft Certified: Power Platform Developer Associate.


                screen shot 2022 01 06 at 10 17 14 pm
                Figure: Get the poster to see Microsoft's certifications

                Check the Become Microsoft Certified poster for details of exams required for each of the certifications.

                Preparing for exams can involve a lot of work, and in some cases stress and anxiety. But remember, you're not in school anymore! You've chosen to take this exam, and no one is forcing you. So just sit back and enjoy the journey - you should feel excited by the new skills you will soon learn. If you want some great advice and tips, be sure to check out Successfully Passing Microsoft Exams by @JasonTaylorDev.

                Good luck!

              20. Do you add branding to screenshots?

                It is great when people use screenshots. You can communicate better by using screenshots with balloons and other visual elements (arrows, shapes, and highlights) instead of only text. Read the benefits of using screenshots.

                We recommend you define a standard style for your visual elements by changing the default colors and shapes according to your branding.

                See some examples of different visual elements under same branding:

                screenshot1
                Figure: Indicate someone to enter the mobile field. Do you use a red arrow?

                screenshot2
                Figure: Or do you like the red box?

                screenshot3
                Figure: Or do you like the yellow highlight?

                More Information on SSW Branding

                You can automatically have the SSW Snagit presets on sign-in via a script.

                Instructions to create and use Snagit themes can be found at Quick Style Themes Tutorial.

              21. Do you know where to add style files for deployment?

                When a designer (or a developer) adds style (CSS) files to SharePoint - care must be taken regarding where the files are placed:

                • Some places are are not suitable because they are not good for deployment
                • Other places may have permission issues - designers can't access them
                • Some files are part of the site definition and should not be customized

                So the rules are:

                1. Never modify out of the box SharePoint files in /Style Library/ - those files are part of the site definition, if you customize them they are hard to deploy
                2. Start with a clean, minimal masterpage
                3. Create and reference your own CSS files and put them under /Style Library/CSS/<client>/
                4. You may want to further divide your CSS paths according to the areas of the site that your CSS is designed for:

                  E.g. /Style Library/CSS/SSW/Clients/layout.css

                5. Designers can modify the XSL file as well!

                  put them under /Style Library/XSL Style Sheets/<client>/

              22. Do you use mobile DevOps?

                TODO

              23. Do you use source control for your Power Apps?

                Source control is essential for any software development project, and Microsoft Power Apps solutions are no exception. Here are some reasons why you would want to use source control like GitHub or Azure DevOps for your Power Apps projects:

                • Versioning - Keep track of changes made to your Power Apps solutions over time.
                • Collaboration - Enable multiple developers to work on the same Power Apps project simultaneously.
                • Audit Trail - Maintain a history of who made what changes and when.
                • Rollback - Easily revert to a previous version if something goes wrong.
                • Branching and Merging - Work on new features or bug fixes in isolation before integrating them into the main project.
                • Automated Deployment - Integrate with CI/CD pipelines for automated testing and deployment.

                How to Implement Source Control

                Exporting Power Apps Solution

                1. Open Power Platform CLI: Open a terminal and navigate to your project directory.
                2. Export Solution: Use the Power Platform CLI to export your Power Apps solution. This will create a .zip file or a directory structure that represents your solution.

                  pac solution export --path ./YourSolutionName --name YourSolutionName

                Using GitHub

                1. Initialize a Git Repository: Navigate to your project directory and run git init.

                  git init
                2. Add Remote Repository: Add GitHub as the remote repository.

                  git remote add origin <Your-GitHub-Repository-URL>
                3. Commit and Push: Add your Power Apps exported solution files to the Git repository, commit them, and push to GitHub.

                  git add .
                  git commit -m "Initial commit"
                  git push -u origin master

                Using Azure DevOps

                1. Create a New Repository: In Azure DevOps, create a new Git repository under your project.
                2. Clone the Repository: Clone the repository to your local machine.

                  git clone <Your-Azure-DevOps-Repository-URL>
                3. Commit and Push: Add your Power Apps exported solution files to the Git repository, commit them, and push to Azure DevOps.

                  git add .
                  git commit -m "Initial commit"
                  git push -u origin master

                Importing Power Apps Solution

                1. Open Power Platform CLI: Open a terminal and navigate to the directory where your exported solution is stored.
                2. Import Solution: Use the Power Platform CLI to import the solution into another environment.

                  pac solution import --path ./YourSolutionName --name YourSolutionName

                By following these steps, you can implement source control for your Power Apps solutions using either GitHub or Azure DevOps. This will help you manage your project more effectively, collaborate with others, and maintain a history of your changes.

              24. Do you use "var"?

                TODO: Byrden - needs a new home (category) and a complete rewrite
                Old content from Better LINQ on .ASPX pasted below

                        

                Despite what it looks like, the var keyword is not a throwback to the dark ages where we did not have strongly typed variables. It is just a short hand to save developers from typing out the type of a variable.

                IQueryable<Customers> results =
                    from c in dbContext.Customers
                    where c.CompanyName.StartsWith(companyNameTextbox.Text)
                    select c;
                customersBindingSource.DataSource = results;

                Figure: Bad example - You should just use "var" instead of "IQueryable"

                var results =
                    from c in dbContext.Customers
                    where c.CompanyName.StartsWith(companyNameTextbox.Text)
                    select c;
                customersBindingSource.DataSource = results;

                Figure: Good example - Using "var" to save few keystrokes

              25. Do you know what a container is?

                The main benefits about containers is that it can reduce your running and maintanence costs; and hear no more it worked on my machine...

                A container is like a little virtual machine that does not have a operating system installed. They already contains all the libraries that your web application needs.

                Video: What you need to know about Containers (in under 3 minutes!)

                Related keywords: Docker, AKS, Kubernetes, Container Orchestration.

              26. Do you use Word Documents instead of Wiki Pages in SharePoint?

                In the past, Wiki pages were a great way to store content in SharePoint. These days, with advancements in browser and smartphone capabilities, Word Documents are a better place to store content.

                Many articles, like this one from 2017, talk about the benefits of using Wiki Pages over Word Documents - however, these benefits are no longer relevant.

                word 2013 online presentation
                Figure: We've come a long way since Word 2013 Online Presentation

                Let's have a quick look at some of the points that the article above used:

                Accuracy - It's easy to see the Modified date of documents in SharePoint, so you can tell if the Document is the most current one (of course, it should be the only one).

                Load Time - In the past, this was an issue. These days, browsers have no problem loading Word Documents.

                Mobile - Mobile browsers also do a pretty good job of opening Word Documents, and there are the Office and OneDrive apps to make it even easier.

                Note: If you want to create an actual Wiki within SharePoint, Modern Pages may be the way to go - but for general content, Word Documents are best.

                Advantages of Word Documents

                1. One main advantage of Word Documents is the editing capabilities – most people are familiar with the rich document editing features of Word. Documents can be created in the desktop app as it has the most editing capabilities, and can be easily edited in the web version - which is still very good! The SharePoint wiki page editor has fewer features, and can be harder to use.

                sharepoint editor
                Limited SharePoint formatting

                microsoft ribbon
                Microsoft Word's well-known, feature rich formatting options

                1. Versioning is one of SharePoint's strongest features - and it works really well with Word Documents.
                2. Many people are using Microsoft Teams, and storing files in there - sometimes not using SharePoint at all. While Teams still uses SharePoint in the backend, it is much easier to browse through Documents than link and view Wiki Pages.

                teams browsing files
                Figure: Browsing files in Teams

                1. OneDrive allows you to sync files from SharePoint libraries to your computer. This is very useful if you need offline access, or if users prefer to work from files within File Explorer in Windows, or Finder on macOS. This works really well with Word Documents, but is not possible with wiki pages.
              27. Do you know how to use AI tools to generate voices and translations?

                AI tools like Rask, Voice.ai, and ElevenLabs are revolutionizing how we generate voices and translations. They can even create transcripts, making our lives a lot easier. But remember, with great power comes great responsibility! 🦸‍♂️

                rask good v2
                Figure: Good Example - Using Rask to translate an English voiceover into French in the speakers own voice.

                Video - Fix Your Website Chatbot! - English version

                Video - Fix Your Website Chatbot! - French translation

                The Need for Oversight 👀

                While these tools are incredibly helpful, they're not perfect. It's important to double-check their output to ensure accuracy. After all, we wouldn't want any miscommunication, would we? 🙅‍♂️

                rask bad v2
                Figure: Bad Example - Relying solely on AI without any oversight can lead to errors.

                The Ever-Changing AI Landscape 🌅

                The AI landscape is constantly changing and improving. Today's best tool might be tomorrow's old news. So, keep an open mind and be ready to adapt to the latest and greatest! 🔄

              28. Do you avoid using 'Share' functionality with tasks/questions?

                Sharing a file is easy when looking at a file. However the email people get from it will have a notification-looking, which may result in it being overlooked/ignored.

                Because of that, you should avoid using that funcionality if you want people to notice and read your email. A normal email with the link and instructions should be sent instead.

                share screenshot
                Figure: Bad example - Sending an email with a file using the Share funcionality

                share email screenshot
                Figure: Bad example - The email sent has a notification-look meaning it may be ignored

                Note: It is a good idea to review SharePoint and Teams stats and tell users not to use this funcionality by pointing them to this rule.

              29. Do you know how to configure YARP?

                YARP matches routes with specified request patterns and forwards them to their destination based on their clusters.

                Basic Configuration:

                YARP can load proxy configuration from App settings.

                1. appsettings.json
                // appsettings.json
                
                {
                  "ReverseProxy": {
                    "Routes": {
                      "webAppLegacyServePath": {
                        "ClusterId": "webAppLegacyClusterId",
                        "Match": {
                          "Path": "/legacyWebapp/{**catch-all}"
                        }
                      },
                
                    },
                    "Clusters": {
                      "webAppLegacyClusterId": {
                        "Destinations": {
                          "webAppLegacyServePath": {
                            "Address": "http://localhost:5001"
                          }
                        }
                      },
                     
                    }
                  }
                }
                1. Load Configuration in ASP.NET Application:
                  To configure a YARP reverse proxy, load settings from the configuration.
                var builder = WebApplication.CreateBuilder(args);
                builder.Services.AddReverseProxy().LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
                1. Add YARP Middleware:
                  Configure MapReverseProxy() middleware in the application's pipeline to handle incoming requests.
                var app = builder.Build();
                app.MapReverseProxy();
                app.Run();

                We can configure YARP using a code-based approach. It's suggested to load the proxy configuration by using IProxyConfigProviderin your code. This is handy when you need a flexible proxy setup that matches your application's unique requirements.

                Advantages:

                • Dynamic configuration updates: In-memory configuration allows to store configuration in the application's memory, making it dynamically accessible for modifications and updates. It improves the performance by significantly reducing the time required to apply configuration updates and reduces the latency by eliminating the need for the application to restart or for service disruptions.
                • Strong typing: Code-based configuration allows to define configuration using strongly typed objects, which eliminates the risk of typos or misconfigurations. This improves code maintainability and reduces the likelihood of runtime errors.

                Disadvantages:

                • Boilerplate code: Need to add more code to support this approach, increasing the overall size and complexity of the codebase.

                1. Defining routes and clusters:

                var webRoutes = new List<RouteConfig>
                        {
                                // Route for Legacy WebApp
                                new()
                                {
                                    RouteId = "webAppLegacyServePath",
                                    ClusterId = webAppLegacyClusterId,
                                    Match = new RouteMatch
                                    {
                                        Path = "/legacyWebapp/{**catch-all}",
                                    },
                                },
                        };
                
                var webClusters = new List<ClusterConfig>
                        {  
                            // Cluster for Legacy WebApp
                
                            new()
                            {
                                ClusterId = webAppLegacyClusterId,
                                Destinations = new Dictionary<string, DestinationConfig>
                                {
                                    {"webAppLegacyServePath", new DestinationConfig{ Address = webAppLegacyAddress } }
                                }
                            },
                        };

                2. Load configuration:

                services
                    .AddReverseProxy()
                    .Services.AddSingleton<IProxyConfigProvider>(
                    new YarpInMemoryConfiguration(webRoutes, webClusters));
                // YarpInMemoryConfiguration is the boilerplate class, see the repo for more details.

                Check out the Yarp Sample Solution to learn more about how it works.

              30. Do you know why YARP is awesome?

                YARP, or Yet Another Reverse Proxy, is a .NET toolkit designed for building fast proxy servers. It seamlessly integrates with ASP.NET and .NET infrastructure, offering easy customization to suit specific deployment needs. YARP operates within the ASP.NET pipeline, handling incoming requests and utilizing its sub-pipeline for proxying requests to backend servers. Users can extend functionality by adding or replacing modules as required.
                YARP uses the concept of Routes to represent request patterns for the proxy and Clusters to represent the services to forward those requests. reverse proxy Figure: YARP is a reverse proxy that acts as the public endpoint for a site or service and forwards calls to backend servers. Source: Announcing YARP 1.0 Release

                Advantages of YARP:

                Numerous API gateways and reverse proxy implementations, including NGINX and Ocelot, are already available for use. However, YARP distinguishes itself with its unique attributes. YARP seamlessly integrates into the ASP.NET environment, offering effortless customization to meet specific requirements. YARP offers a comprehensive set of features for building and managing reverse proxy solutions, including:

                • Dynamic route definitions: Enables the definition of routes in a dynamic configuration, allowing flexibility in specifying how incoming requests are handled.
                • Extensible pipeline model: Customize the proxy behavior using a modular pipeline architecture.
                • Load balancing: Employ various load-balancing algorithms to distribute traffic effectively.
                • Session Affinity: Ensures that requests from a specific client are consistently directed to the same destination server, promoting continuity and predictability in the user experience.
                • Request and response transformation: Allows developers to apply transformations to requests sent to or responses received from destination servers. This feature facilitates the customization of data before it reaches its destination.
                • Route-level authorization and CORS: Permits the specification of authorization and Cross-Origin Resource Sharing (CORS) settings on a per-route basis. This ensures fine-grained control over access and security policies for different routes.
              31. LinkedIn – Do you use Creator mode?

                Creator mode is a profile setting that can help you grow your reach, influence and audience on LinkedIn.

                You can turn on creator mode to get access to additional tools and features that help you create content and grow your audience base on LinkedIn.

                For example, you can add topics you post the most about in the form of hashtags.

                Picture1
                Figure: Bad example - no hashtags

                How to turn on Creator mode

                1. Go to your LinkedIn profile
                2. Scroll down and click 'Creator mode'

                Picture222
                Figure: Where to find Creator mode

                1. Include relevant hashtags
                2. Click 'turn on'

                Picture3
                Figure: Hashtag example

                Picture4
                Figure: Good example- Profile with Creator mode enabled

              32. Do you know when to use scoped CSS?

                On large frontend projects with lots of components it's common to have issues with your CSS classes overwriting each other or conflicting. There are a few different ways to solve this.

                It can be very frustrating when CSS classes overwrite and behave differently to how we want them to.

                overwritten style
                Figure: Overwritten CSS can make our site styles break

                Debugging this can be a nightmare on larger projects with huge amounts of styling as you need to comb through the entire file to find what's overwriting the style.

                h1 {
                  color: red;
                }
                
                /* 3000 more lines of code... */

                Utility Classes

                One solution to this problem is using a CSS framework such as Tailwind.

                Benefits

                • Faster setup
                • Simple to use
                • Faster development speed once devs are familiar with it

                Disadvantages

                • Poor readability as you add more classes
                • Learning curve for class names

                Scoped CSS

                Most frontend frameworks such as Blazor, Angular and React support scoped CSS, allowing you set CSS classes on a component level.

                When you create a scoped CSS file, those classes are only accessible to the component that the CSS file is associated with.

                This is useful as you don't need to worry about class names or CSS values conflicting with eachother like you would if all the classes are set at the root level. It also makes it much easier to change styling on individual components without breaking the styling on other classes.

                Benefits

                • Can reuse class names in each component without conflicts

                Disadvantages

                • Styles have to be manually written

                Blazor

                In Blazor scoped CSS files are associated with components with dot syntax.
                For example, if we have a component called MyComponent.razor, we can create a file called MyComponent.razor.css and the classes set in that file will only apply to MyComponent.

                Inheritance

                Blazor supports having scoped CSS classes inherited by child components.

                <MyComponent>
                  <MyChildComponent />
                </MyComponent>

                In MyComponent.razor.cs, if we use the ::deep psuedo selector on a class, it will be applied to MyChildComponent.

                ::deep .myClass {
                  /* Styling goes here */
                }

                Blazor CSS Isolation - Microsoft Docs

                Angular

                When you create a component in Angular, a scoped CSS file is automatically created for it along with the HTML and TypeScript files.

                myComponent
                |- myComponent.component.html
                |- myComponent.component.css
                |- myComponent.component.spec.ts
                |- myComponent.component.ts
                Inheritance

                Angular also offers the ::ng-deep pseudo selector, but there are some considerations when using this.

                Component Styles - Angular Docs

                React

                We can achieve local, component-level styles in React by using importing from a file ending in module.css:

                /* component.module.css */
                
                .heading {
                  font-weight: 700;
                }
                import styles from "component.module.css";
                
                const Component = () => {
                  return (
                    <h1 style={styles.heading}>Bold Heading Here</h1>
                  )
                }

                This will then render the element with a component specific class, differentiated from other .heading elements:

                <h1 class="Component_heading__nEBhk">Bold Heading Here</h1>
              We open source. Powered by GitHub