{"id":189254,"date":"2018-06-26T11:00:42","date_gmt":"2018-06-26T18:00:42","guid":{"rendered":"http:\/\/developer.salesforce.com\/blogs\/?p=189254"},"modified":"2025-11-05T02:23:31","modified_gmt":"2025-11-05T09:23:31","slug":"working-with-modular-development-and-unlocked-packages-part-4","status":"publish","type":"post","link":"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4","title":{"rendered":"Working with Modular Development and Unlocked Packages: Part 4"},"content":{"rendered":"<p>This is the fourth installment in a series exploring how to begin working with your apps in modular pieces, incorporating packages into your app development lifecycle, and what packaging may mean for your team\u2019s change management and release processes. Over the course of this series, we\u2019ll talk about:<\/p>\n<ul>\n<li><a href=\"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-1.html\">Part 1<\/a>: What even is a package, anyway? How can you start to experiment with segmenting your org?<\/li>\n<li><a href=\"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-2.html\">Part 2<\/a>: How can you start to organize metadata from an app, let alone an entire org, into packages? How do you tackle organizing your metadata and projects in source control?<\/li>\n<li><a href=\"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-3.html\">Part 3<\/a>: What do these changes mean for app builder workflows? What will happen if I install an unlocked package into my production org today?<\/li>\n<li>Part 4: How can you define a successful Git branching strategy that works best for most team sizes? How, when and where should packaging be added to your continuous deployment?<\/li>\n<\/ul>\n<p>After you have untangled your application and set up your package directories and initial package version, it&#8217;s time to work on a strategy for managing your package versions and active development projects in your source control systems. Having a good and practical Git strategy will be crucial for your Continuous Integration and Continuous Deployment workflows. This blog post covers how to create such a strategy.<\/p>\n<h2>An intro to Git branching<\/h2>\n<p>Source control (and with that, Git) has been around for a long time in the Salesforce world. But for those who haven&#8217;t worked with Git and the Salesforce Platform yet, let&#8217;s look at briefly at a widely adapted Git branching strategy.<\/p>\n<p>\n\t\t\t  <span class=\"postimagessection_specify aligncenter\" >\n\t\t\t    <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/d259t2jj6zp7qm.cloudfront.net\/images\/c_scale%2Cw_900-git_model_bjoajz.png\" class=\"postimages\" width=\"900\" height=\"1193\" alt=\"\" \/>\n\t\t\t  <\/span>\n\t\t\t<\/p>\n<p style=\"text-align: center\">(<a href=\"https:\/\/nvie.com\/posts\/a-successful-git-branching-model\/\">Photo source<\/a>)<\/p>\n<p>In a nutshell:<\/p>\n<ul>\n<li>All active development work happens in the <code>development<\/code> branch.<\/li>\n<li>Creating new features or fixing bugs happens in dedicated <code>feature<\/code> or <code>hotfix<\/code> branches. Those branches inherit from <code>development<\/code>.<\/li>\n<li>After the feature\/bug has been successfully tested, the code gets merged back into the <code>development<\/code> branch.<\/li>\n<li>Code that gets shipped into production gets merged from <code>development<\/code> into <code>master<\/code>. As versioning is important, the <code>master<\/code> branch gets tagged with a version number.<\/li>\n<\/ul>\n<p>While that itself sounds simple, it can get a bit complicated, especially when you have multiple branches running at the same time (which is not uncommon in software development, especially when you work in a team).<\/p>\n<p>Here&#8217;s a small real-world example from a <i>code race<\/i> that I did some years ago.<\/p>\n<p>\n\t\t\t  <span class=\"postimagessection_specify aligncenter\" >\n\t\t\t    <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/d259t2jj6zp7qm.cloudfront.net\/images\/v1530033869-git-run_kj0s8a.png\" class=\"postimages\" width=\"465\" height=\"85\" alt=\"\" \/>\n\t\t\t  <\/span>\n\t\t\t<\/p>\n<p>Blue is <code>master<\/code>, green is <code>develop<\/code>, the other are <code>feature<\/code> and <code>hotfix<\/code> branches.<\/p>\n<h2>Git branching strategy<\/h2>\n<p>Building upon that branching strategy and your own company&#8217;s requirements, you may have (or should) extend the model. Before you start designing your Git strategy you should have answers to the following questions:<\/p>\n<ul>\n<li>At what stage in the process should automated tests run?<\/li>\n<li>When should unlocked package versions be created, tested, and cleaned up?<\/li>\n<li>How should promotion (aka installation) of package versions to environments like QA sandboxes or even production be executed?<\/li>\n<\/ul>\n<p>You may answer some or all of those questions with \u201cI want that on every commit.\u201d But then you&#8217;ll experience the delay that it takes to create a new package version before it can be installed. Waiting eight minutes before you get feedback if everything is okay from your CI system can feel like a long time, even more if you commit multiple times a day. Last but not least, there are daily limits for scratch org creation and package version creation request, so you don&#8217;t want to have an overly aggressive package version strategy, as it will clutter your environment and eat up your available resources.<\/p>\n<p>What if you want to test package version creation and installation \u201conly\u201d on every Pull Request? On what branches? <i>Develop<\/i>, <i>master<\/i>, all <i>feature<\/i> branches? There are many options. You want to find out if something is broken or may be broken as early as possible in your development workflow. However, you also don&#8217;t want to get slowed down by the process.<\/p>\n<p>For our <a href=\"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/announcing-the-easy-spaces-app.html\">Easy Spaces app<\/a> we defined the following naming conventions for our Git branches:<\/p>\n<ul>\n<li><code>feature\/<\/code><code><i>package-name\/*<\/i><\/code> &#8211; a feature (and bug fix) branch for an individual package<\/li>\n<li><code>packaging\/<i>package-name\/*<\/i><\/code> &#8211; a per package branch that tests package creation and installation for an individual package<\/li>\n<li><code>packaging<\/code> &#8211; this branch reflects the code change for partial sandbox or UAT sandbox<\/li>\n<li><code>develop<\/code> &#8211; this branch reflects pre-production, for example a full-copy sandbox<\/li>\n<li><code>master<\/code> &#8211; this branch is what reflects production<\/li>\n<\/ul>\n<p>For our sample application, we settled with what are standard names for Git branches, like <i>develop<\/i> or <i>master<\/i>. You should use names that make most sense to you and your organization. For example, <i>packaging<\/i> could be <i>partial-sandbox<\/i> and <i>develop<\/i> could be <i>full-sandbox<\/i>. There&#8217;s no limit in how you can customize. Just keep in mind that it needs to be manageable.<\/p>\n<p>We also defined pre-conditions that should be met before a branch gets merged or code gets committed as well as which specific tasks should run. Here&#8217;s an excerpt, based on the <a href=\"https:\/\/github.com\/trailheadapps\/easy-spaces\/blob\/master\/.circleci\/config.yml\">used CircleCI workflow<\/a> (note that this graphic doesn&#8217;t include branch filtering yet):<\/p>\n<p>\n\t\t\t  <span class=\"postimagessection_specify aligncenter\" >\n\t\t\t    <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/d259t2jj6zp7qm.cloudfront.net\/images\/c_scale%2Cw_900-workflow_haclgc.png\" class=\"postimages\" width=\"900\" height=\"141\" alt=\"\" \/>\n\t\t\t  <\/span>\n\t\t\t<\/p>\n<p><code>feature\/<i>package-name\/*<\/i><\/code> branches &#8211; after every commit<\/p>\n<ul>\n<li>Deploy all package folders using <i>sfdx force:source:push.<\/i><\/li>\n<li>Run all tests, like Apex and\/or Lightning Testing Service.<\/li>\n<li>Don&#8217;t create any package versions.<\/li>\n<li>Delete scratch org.<\/li>\n<\/ul>\n<p><code>packaging\/<i>package-name\/*<\/i><\/code> branches &#8211; on every pull request coming in from a <code>feature\/<i>package-name<\/i><\/code> branch<\/p>\n<ul>\n<li>Create a new package version for <i>package-name.<\/i><\/li>\n<li>Deploy in package directory order (based on <code>sfdx-project.json<\/code>) to sandbox org. All package directories will be deployed as packages.<\/li>\n<li>Run all tests, like Apex and\/or Lightning Testing Service.<\/li>\n<li>Delete scratch org.<\/li>\n<\/ul>\n<p><code>packaging<\/code> branch &#8211; on every pull request coming in from a <code>packaging\/<i>package-name<\/i><\/code> branch<\/p>\n<ul>\n<li>Deploy in package directory order (based on <code>sfdx-project.json<\/code>) to sandbox org. All package directories will be deployed as packages.<\/li>\n<li>Run all tests, like Apex and\/or Lightning Testing Service.<\/li>\n<\/ul>\n<p><code>develop<\/code> branch &#8211; on every pull request coming in from the <code>packaging<\/code> branch<\/p>\n<ul>\n<li>Deploy in package directory order (based on <code>sfdx-project.json<\/code>) to full-copy sandbox. All package directories will be deployed as packages.<\/li>\n<li>Run all tests, like Apex and\/or Lightning Testing Service.<\/li>\n<\/ul>\n<p><code>master<\/code> branch<\/p>\n<ul>\n<li>No actions (we&#8217;ll explain later in this post)<\/li>\n<\/ul>\n<p>Some of these decisions can and will be different for your organization. For example, you may want to run package creation also at a feature level if you want to make sure that your development team (or yourself) doesn&#8217;t introduce unpackageable features. For us, we decided that developers consult the documentation around <a href=\"https:\/\/developer.salesforce.com\/docs\/atlas.en-us.sfdx_dev.meta\/sfdx_dev\/sfdx_dev_supported_mdapi_types.htm\">supported Metadata types<\/a> before they start working on things. Relevant metadata which is yet not packageable should be moved into separate folders, like we did with the branding images for <a href=\"https:\/\/help.salesforce.com\/articleView?id=brand_your_org_in_lightning_experience.htm&amp;type=5\">Salesforce Branding &amp; Theming<\/a> in the <a href=\"https:\/\/github.com\/trailheadapps\/easy-spaces\/tree\/master\/es-images\">Easy Spaces sample app<\/a>.<\/p>\n<h2>Branches and tasks in detail<\/h2>\n<p>Let&#8217;s have a more detailed look at the why and how we made the aforementioned decisions for branches and their tasks.<\/p>\n<p><code>feature\/<i>package-name\/*<\/i><\/code> branches<br \/>\nFor these branches we decided to push all source using <code>sfdx force:source:push <\/code>and wouldn&#8217;t use any packages. This decision is based on the following reasons:<\/p>\n<ul>\n<li>We don&#8217;t want to create package versions for every \u201csimple\u201d commit. At this branch level, working on feature code has higher priority than package testing.<\/li>\n<li>We use dependencies for packages. Which means you cannot, for example, use <code>sfdx force:source:push<\/code> on the <i>es-base-objects<\/i> folder and then install <i>es-base-code<\/i> via package installation. This will fail because the <i>es-base-code<\/i> package has the dependency of an installed <i>es-base-objects<\/i> package.<\/li>\n<\/ul>\n<p>If you think: \u201cHey, I can just remove the dependencies in the <code>sfdx-project.json<\/code>, and then do a push\/install package mix&#8230;\u201d &#8211; that won&#8217;t work. The dependencies are part of the created package version, so any manipulation in the <code>sfdx-project.json<\/code> won&#8217;t have an effect. Been there, done that.<\/p>\n<p>Once we&#8217;re done with the feature we&#8217;re also modifying the package version creation command in the <a href=\"https:\/\/github.com\/trailheadapps\/easy-spaces\/tree\/master\/.circleci\/config.yml\">CI configuration<\/a> with data like the version number, description and so on. This step is important because when we move the code via a PR into the ancestral <code>packaging\/<i>package-name<\/i>\/<\/code> branch, the package version creation process kicks off.<\/p>\n<p><code>packaging\/<i>package-name\/*<\/i><\/code> branches<br \/>\nAfter a feature is completely developed, we move it to its <code>packaging<\/code> branch. This is also where the focus goes from \u201ca feature works\u201d to \u201ca feature works AND gets packaged\u201d.<\/p>\n<p>You can see this branch as the intermediary between active development and preparation for first sandbox testing. In your organization you may make the decision to skip this branch and instead let someone manually create the package version. That&#8217;s totally legit. Choose what works best for you.<\/p>\n<p>We&#8217;ve chosen our approach because it allows us to see all involved steps and their output within the UI of the chosen CI solution. For this we&#8217;re <a href=\"https:\/\/github.com\/trailheadapps\/easy-spaces\/blob\/master\/scripts\/packagingDeployment.sh\">executing a script<\/a> that can be used via CI and can be also manually invoked from the developers workstation.<\/p>\n<p><code>packaging<\/code> branch<br \/>\nOnce we know that the new feature works (thanks to testing) and that we have a new package, we want to install it in a sandbox. That can be a partial sandbox or a UAT sandbox for example. We&#8217;ve settled for our use case to name the branch \u201cpackaging,\u201d but you can (and should!) use a name that reflects your stage in the process, i.e. \u201cuat-sandbox\u201d.<\/p>\n<p>It&#8217;s noteworthy that we focus on installation of the packages. For that, we fetch all to be used versions for the packages via a bash script and deploy them. That way the sandbox gets hit with the latest code across all folders specified as package directories in the <code>sfdx-project.json<\/code> configuration.<\/p>\n<p><code>develop<\/code> branch<br \/>\nThe <i>develop<\/i> branch reflects what could be for you a full-copy sandbox. The steps are the same as in the <i>packaging<\/i> branch with the focus on package installation. The steps don&#8217;t change. We only move to the next stage of testing environment before we move to production.<\/p>\n<p><code>master<\/code> branch<br \/>\nNow, if you remember, there&#8217;s still the <i>master<\/i> branch \u2014 with no actions. There&#8217;s also still the outstanding task of bringing the new package version(s) to production. This is the stage where we leave the path of continuous deployment and do some manual steps.<\/p>\n<p>At this point some decisions need to be made. You have to define which package version is the one that you want to promote to production using the <code>sfdx force:package:version:promote<\/code> command. You may want to say \u201cthe latest,\u201d but as development doesn&#8217;t stop in the world of continuous development, there maybe already the next version in testing in the packaging branch, depending on your strategy. So you have to manually pick a version and that&#8217;s what we recommend for now. Make a manual and educated decision because that will affect production and with that 5 or 5,000 users. After the promotion and installation of the package, submit the code to the <code>master<\/code> branch.<\/p>\n<p>What&#8217;s always recommended is that you make use of tags on the master branch when you release a new version. That way you have your source of truth aligned with what is in production. Plus, you can go back anytime to see \u2014 and compare \u2014 changes between your custom application releases.<\/p>\n<p>You remember the code race image? Here&#8217;s another one with multiple simultaneous branches going on. You want to make sure that always the correct one becomes the master.<\/p>\n<p>\n\t\t\t  <span class=\"postimagessection_specify aligncenter\" >\n\t\t\t    <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/d259t2jj6zp7qm.cloudfront.net\/images\/v1530034010-git-art_bppx8a.png\" class=\"postimages\" width=\"357\" height=\"89\" alt=\"\" \/>\n\t\t\t  <\/span>\n\t\t\t<\/p>\n<h2>Your next steps<\/h2>\n<p>Depending on your organizational requirements your Git branching strategy may be very similar or different to our approach. With the currently available capabilities of unlocked packages, not everything may work right away in that approach. It can even be different depending on the type of application or the level of modularization that you want or can introduce with unlocked packages.<\/p>\n<p>What is most important is that you start thinking about a meaningful branching strategy \u2014 the sooner the better, as it will simplify your daily application development workflows. This not only applies to just unlocked packages but development in general.<\/p>\n<p>If you need a refresh on Git, checkout the <a href=\"https:\/\/trailhead.salesforce.com\/modules\/git-and-git-hub-basics\">Git and Github Basics<\/a> module on Trailhead. Take a look at the <a href=\"https:\/\/github.com\/trailheadapps\/easy-spaces\">Easy Spaces sample app<\/a>, which focuses on the modularization with unlocked packages. Finally, start experimenting with different Git branch strategies to find the perfect fit for your organization.<\/p>\n<h2>About the author<\/h2>\n<p>Ren\u00e9 Winkelmeyer works as Principal Developer Evangelist at Salesforce. He focuses on enterprise integrations, mobile, and security with the Lightning Platform. You can follow him on Twitter <a href=\"https:\/\/twitter.com\/muenzpraeger\">@muenzpraeger<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is the fourth installment in a series exploring how to begin working with your apps in modular pieces, incorporating packages into your app development lifecycle, and what packaging may mean for your team\u2019s change management and release processes. Over the course of this series, we\u2019ll talk about: Part 1: What even is a package, [&hellip;]<\/p>\n","protected":false},"author":3071,"featured_media":189260,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2766],"tags":[2568],"coauthors":[],"class_list":["post-189254","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tutorials","tag-packaging-unlocked"],"podcast_audio":{"audio_url":"","duration":""},"featured_image":"https:\/\/d259t2jj6zp7qm.cloudfront.net\/images\/v1530034177-trailhead_module_git_and_github_basics_f1rqxy.png?w=200","related_posts":[],"unstyled_content":"<p>This is the fourth installment in a series exploring how to begin working with your apps in modular pieces, incorporating packages into your app development lifecycle, and what packaging may mean for your team\u2019s change management and release processes. Over the course of this series, we\u2019ll talk about:<\/p>\n<ul>\n<li><a href=\"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-1.html\">Part 1<\/a>: What even is a package, anyway? How can you start to experiment with segmenting your org?<\/li>\n<li><a href=\"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-2.html\">Part 2<\/a>: How can you start to organize metadata from an app, let alone an entire org, into packages? How do you tackle organizing your metadata and projects in source control?<\/li>\n<li><a href=\"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-3.html\">Part 3<\/a>: What do these changes mean for app builder workflows? What will happen if I install an unlocked package into my production org today?<\/li>\n<li>Part 4: How can you define a successful Git branching strategy that works best for most team sizes? How, when and where should packaging be added to your continuous deployment?<\/li>\n<\/ul>\n<p>After you have untangled your application and set up your package directories and initial package version, it&#8217;s time to work on a strategy for managing your package versions and active development projects in your source control systems. Having a good and practical Git strategy will be crucial for your Continuous Integration and Continuous Deployment workflows. This blog post covers how to create such a strategy.<\/p>\n<h2>An intro to Git branching<\/h2>\n<p>Source control (and with that, Git) has been around for a long time in the Salesforce world. But for those who haven&#8217;t worked with Git and the Salesforce Platform yet, let&#8217;s look at briefly at a widely adapted Git branching strategy.<\/p>\n<p>\n\t\t\t  <span >\n\t\t\t    <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/d259t2jj6zp7qm.cloudfront.net\/images\/c_scale%2Cw_900-git_model_bjoajz.png\" width=\"900\" height=\"1193\" alt=\"\" \/>\n\t\t\t  <\/span>\n\t\t\t<\/p>\n<p>(<a href=\"https:\/\/nvie.com\/posts\/a-successful-git-branching-model\/\">Photo source<\/a>)<\/p>\n<p>In a nutshell:<\/p>\n<ul>\n<li>All active development work happens in the <code>development<\/code> branch.<\/li>\n<li>Creating new features or fixing bugs happens in dedicated <code>feature<\/code> or <code>hotfix<\/code> branches. Those branches inherit from <code>development<\/code>.<\/li>\n<li>After the feature\/bug has been successfully tested, the code gets merged back into the <code>development<\/code> branch.<\/li>\n<li>Code that gets shipped into production gets merged from <code>development<\/code> into <code>master<\/code>. As versioning is important, the <code>master<\/code> branch gets tagged with a version number.<\/li>\n<\/ul>\n<p>While that itself sounds simple, it can get a bit complicated, especially when you have multiple branches running at the same time (which is not uncommon in software development, especially when you work in a team).<\/p>\n<p>Here&#8217;s a small real-world example from a <i>code race<\/i> that I did some years ago.<\/p>\n<p>\n\t\t\t  <span >\n\t\t\t    <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/d259t2jj6zp7qm.cloudfront.net\/images\/v1530033869-git-run_kj0s8a.png\" width=\"465\" height=\"85\" alt=\"\" \/>\n\t\t\t  <\/span>\n\t\t\t<\/p>\n<p>Blue is <code>master<\/code>, green is <code>develop<\/code>, the other are <code>feature<\/code> and <code>hotfix<\/code> branches.<\/p>\n<h2>Git branching strategy<\/h2>\n<p>Building upon that branching strategy and your own company&#8217;s requirements, you may have (or should) extend the model. Before you start designing your Git strategy you should have answers to the following questions:<\/p>\n<ul>\n<li>At what stage in the process should automated tests run?<\/li>\n<li>When should unlocked package versions be created, tested, and cleaned up?<\/li>\n<li>How should promotion (aka installation) of package versions to environments like QA sandboxes or even production be executed?<\/li>\n<\/ul>\n<p>You may answer some or all of those questions with \u201cI want that on every commit.\u201d But then you&#8217;ll experience the delay that it takes to create a new package version before it can be installed. Waiting eight minutes before you get feedback if everything is okay from your CI system can feel like a long time, even more if you commit multiple times a day. Last but not least, there are daily limits for scratch org creation and package version creation request, so you don&#8217;t want to have an overly aggressive package version strategy, as it will clutter your environment and eat up your available resources.<\/p>\n<p>What if you want to test package version creation and installation \u201conly\u201d on every Pull Request? On what branches? <i>Develop<\/i>, <i>master<\/i>, all <i>feature<\/i> branches? There are many options. You want to find out if something is broken or may be broken as early as possible in your development workflow. However, you also don&#8217;t want to get slowed down by the process.<\/p>\n<p>For our <a href=\"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/announcing-the-easy-spaces-app.html\">Easy Spaces app<\/a> we defined the following naming conventions for our Git branches:<\/p>\n<ul>\n<li><code>feature\/<\/code><code><i>package-name\/*<\/i><\/code> &#8211; a feature (and bug fix) branch for an individual package<\/li>\n<li><code>packaging\/<i>package-name\/*<\/i><\/code> &#8211; a per package branch that tests package creation and installation for an individual package<\/li>\n<li><code>packaging<\/code> &#8211; this branch reflects the code change for partial sandbox or UAT sandbox<\/li>\n<li><code>develop<\/code> &#8211; this branch reflects pre-production, for example a full-copy sandbox<\/li>\n<li><code>master<\/code> &#8211; this branch is what reflects production<\/li>\n<\/ul>\n<p>For our sample application, we settled with what are standard names for Git branches, like <i>develop<\/i> or <i>master<\/i>. You should use names that make most sense to you and your organization. For example, <i>packaging<\/i> could be <i>partial-sandbox<\/i> and <i>develop<\/i> could be <i>full-sandbox<\/i>. There&#8217;s no limit in how you can customize. Just keep in mind that it needs to be manageable.<\/p>\n<p>We also defined pre-conditions that should be met before a branch gets merged or code gets committed as well as which specific tasks should run. Here&#8217;s an excerpt, based on the <a href=\"https:\/\/github.com\/trailheadapps\/easy-spaces\/blob\/master\/.circleci\/config.yml\">used CircleCI workflow<\/a> (note that this graphic doesn&#8217;t include branch filtering yet):<\/p>\n<p>\n\t\t\t  <span >\n\t\t\t    <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/d259t2jj6zp7qm.cloudfront.net\/images\/c_scale%2Cw_900-workflow_haclgc.png\" width=\"900\" height=\"141\" alt=\"\" \/>\n\t\t\t  <\/span>\n\t\t\t<\/p>\n<p><code>feature\/<i>package-name\/*<\/i><\/code> branches &#8211; after every commit<\/p>\n<ul>\n<li>Deploy all package folders using <i>sfdx force:source:push.<\/i><\/li>\n<li>Run all tests, like Apex and\/or Lightning Testing Service.<\/li>\n<li>Don&#8217;t create any package versions.<\/li>\n<li>Delete scratch org.<\/li>\n<\/ul>\n<p><code>packaging\/<i>package-name\/*<\/i><\/code> branches &#8211; on every pull request coming in from a <code>feature\/<i>package-name<\/i><\/code> branch<\/p>\n<ul>\n<li>Create a new package version for <i>package-name.<\/i><\/li>\n<li>Deploy in package directory order (based on <code>sfdx-project.json<\/code>) to sandbox org. All package directories will be deployed as packages.<\/li>\n<li>Run all tests, like Apex and\/or Lightning Testing Service.<\/li>\n<li>Delete scratch org.<\/li>\n<\/ul>\n<p><code>packaging<\/code> branch &#8211; on every pull request coming in from a <code>packaging\/<i>package-name<\/i><\/code> branch<\/p>\n<ul>\n<li>Deploy in package directory order (based on <code>sfdx-project.json<\/code>) to sandbox org. All package directories will be deployed as packages.<\/li>\n<li>Run all tests, like Apex and\/or Lightning Testing Service.<\/li>\n<\/ul>\n<p><code>develop<\/code> branch &#8211; on every pull request coming in from the <code>packaging<\/code> branch<\/p>\n<ul>\n<li>Deploy in package directory order (based on <code>sfdx-project.json<\/code>) to full-copy sandbox. All package directories will be deployed as packages.<\/li>\n<li>Run all tests, like Apex and\/or Lightning Testing Service.<\/li>\n<\/ul>\n<p><code>master<\/code> branch<\/p>\n<ul>\n<li>No actions (we&#8217;ll explain later in this post)<\/li>\n<\/ul>\n<p>Some of these decisions can and will be different for your organization. For example, you may want to run package creation also at a feature level if you want to make sure that your development team (or yourself) doesn&#8217;t introduce unpackageable features. For us, we decided that developers consult the documentation around <a href=\"https:\/\/developer.salesforce.com\/docs\/atlas.en-us.sfdx_dev.meta\/sfdx_dev\/sfdx_dev_supported_mdapi_types.htm\">supported Metadata types<\/a> before they start working on things. Relevant metadata which is yet not packageable should be moved into separate folders, like we did with the branding images for <a href=\"https:\/\/help.salesforce.com\/articleView?id=brand_your_org_in_lightning_experience.htm&amp;type=5\">Salesforce Branding &amp; Theming<\/a> in the <a href=\"https:\/\/github.com\/trailheadapps\/easy-spaces\/tree\/master\/es-images\">Easy Spaces sample app<\/a>.<\/p>\n<h2>Branches and tasks in detail<\/h2>\n<p>Let&#8217;s have a more detailed look at the why and how we made the aforementioned decisions for branches and their tasks.<\/p>\n<p><code>feature\/<i>package-name\/*<\/i><\/code> branches<br \/>\nFor these branches we decided to push all source using <code>sfdx force:source:push <\/code>and wouldn&#8217;t use any packages. This decision is based on the following reasons:<\/p>\n<ul>\n<li>We don&#8217;t want to create package versions for every \u201csimple\u201d commit. At this branch level, working on feature code has higher priority than package testing.<\/li>\n<li>We use dependencies for packages. Which means you cannot, for example, use <code>sfdx force:source:push<\/code> on the <i>es-base-objects<\/i> folder and then install <i>es-base-code<\/i> via package installation. This will fail because the <i>es-base-code<\/i> package has the dependency of an installed <i>es-base-objects<\/i> package.<\/li>\n<\/ul>\n<p>If you think: \u201cHey, I can just remove the dependencies in the <code>sfdx-project.json<\/code>, and then do a push\/install package mix&#8230;\u201d &#8211; that won&#8217;t work. The dependencies are part of the created package version, so any manipulation in the <code>sfdx-project.json<\/code> won&#8217;t have an effect. Been there, done that.<\/p>\n<p>Once we&#8217;re done with the feature we&#8217;re also modifying the package version creation command in the <a href=\"https:\/\/github.com\/trailheadapps\/easy-spaces\/tree\/master\/.circleci\/config.yml\">CI configuration<\/a> with data like the version number, description and so on. This step is important because when we move the code via a PR into the ancestral <code>packaging\/<i>package-name<\/i>\/<\/code> branch, the package version creation process kicks off.<\/p>\n<p><code>packaging\/<i>package-name\/*<\/i><\/code> branches<br \/>\nAfter a feature is completely developed, we move it to its <code>packaging<\/code> branch. This is also where the focus goes from \u201ca feature works\u201d to \u201ca feature works AND gets packaged\u201d.<\/p>\n<p>You can see this branch as the intermediary between active development and preparation for first sandbox testing. In your organization you may make the decision to skip this branch and instead let someone manually create the package version. That&#8217;s totally legit. Choose what works best for you.<\/p>\n<p>We&#8217;ve chosen our approach because it allows us to see all involved steps and their output within the UI of the chosen CI solution. For this we&#8217;re <a href=\"https:\/\/github.com\/trailheadapps\/easy-spaces\/blob\/master\/scripts\/packagingDeployment.sh\">executing a script<\/a> that can be used via CI and can be also manually invoked from the developers workstation.<\/p>\n<p><code>packaging<\/code> branch<br \/>\nOnce we know that the new feature works (thanks to testing) and that we have a new package, we want to install it in a sandbox. That can be a partial sandbox or a UAT sandbox for example. We&#8217;ve settled for our use case to name the branch \u201cpackaging,\u201d but you can (and should!) use a name that reflects your stage in the process, i.e. \u201cuat-sandbox\u201d.<\/p>\n<p>It&#8217;s noteworthy that we focus on installation of the packages. For that, we fetch all to be used versions for the packages via a bash script and deploy them. That way the sandbox gets hit with the latest code across all folders specified as package directories in the <code>sfdx-project.json<\/code> configuration.<\/p>\n<p><code>develop<\/code> branch<br \/>\nThe <i>develop<\/i> branch reflects what could be for you a full-copy sandbox. The steps are the same as in the <i>packaging<\/i> branch with the focus on package installation. The steps don&#8217;t change. We only move to the next stage of testing environment before we move to production.<\/p>\n<p><code>master<\/code> branch<br \/>\nNow, if you remember, there&#8217;s still the <i>master<\/i> branch \u2014 with no actions. There&#8217;s also still the outstanding task of bringing the new package version(s) to production. This is the stage where we leave the path of continuous deployment and do some manual steps.<\/p>\n<p>At this point some decisions need to be made. You have to define which package version is the one that you want to promote to production using the <code>sfdx force:package:version:promote<\/code> command. You may want to say \u201cthe latest,\u201d but as development doesn&#8217;t stop in the world of continuous development, there maybe already the next version in testing in the packaging branch, depending on your strategy. So you have to manually pick a version and that&#8217;s what we recommend for now. Make a manual and educated decision because that will affect production and with that 5 or 5,000 users. After the promotion and installation of the package, submit the code to the <code>master<\/code> branch.<\/p>\n<p>What&#8217;s always recommended is that you make use of tags on the master branch when you release a new version. That way you have your source of truth aligned with what is in production. Plus, you can go back anytime to see \u2014 and compare \u2014 changes between your custom application releases.<\/p>\n<p>You remember the code race image? Here&#8217;s another one with multiple simultaneous branches going on. You want to make sure that always the correct one becomes the master.<\/p>\n<p>\n\t\t\t  <span >\n\t\t\t    <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/d259t2jj6zp7qm.cloudfront.net\/images\/v1530034010-git-art_bppx8a.png\" width=\"357\" height=\"89\" alt=\"\" \/>\n\t\t\t  <\/span>\n\t\t\t<\/p>\n<h2>Your next steps<\/h2>\n<p>Depending on your organizational requirements your Git branching strategy may be very similar or different to our approach. With the currently available capabilities of unlocked packages, not everything may work right away in that approach. It can even be different depending on the type of application or the level of modularization that you want or can introduce with unlocked packages.<\/p>\n<p>What is most important is that you start thinking about a meaningful branching strategy \u2014 the sooner the better, as it will simplify your daily application development workflows. This not only applies to just unlocked packages but development in general.<\/p>\n<p>If you need a refresh on Git, checkout the <a href=\"https:\/\/trailhead.salesforce.com\/modules\/git-and-git-hub-basics\">Git and Github Basics<\/a> module on Trailhead. Take a look at the <a href=\"https:\/\/github.com\/trailheadapps\/easy-spaces\">Easy Spaces sample app<\/a>, which focuses on the modularization with unlocked packages. Finally, start experimenting with different Git branch strategies to find the perfect fit for your organization.<\/p>\n<h2>About the author<\/h2>\n<p>Ren\u00e9 Winkelmeyer works as Principal Developer Evangelist at Salesforce. He focuses on enterprise integrations, mobile, and security with the Lightning Platform. You can follow him on Twitter <a href=\"https:\/\/twitter.com\/muenzpraeger\">@muenzpraeger<\/a>.<\/p>\n","acf":{"canonicalid":"","language":"english","audio_url":"https:\/\/a.sfdcstatic.com\/developer-website\/blog-audio\/189254\/189254.mp3","hash":"e4c08498d902e72e9834ea966862096a","transcription_id":"60e6c847-cc31-4bf7-94fe-c3dbe7039996","ready":true},"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v24.3 (Yoast SEO v25.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Working with Modular Development and Unlocked Packages: Part 4 - Salesforce Developers Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Working with Modular Development and Unlocked Packages: Part 4\" \/>\n<meta property=\"og:description\" content=\"This is the fourth installment in a series exploring how to begin working with your apps in modular pieces, incorporating packages into your app development lifecycle, and what packaging may mean for your team\u2019s change management and release processes. Over the course of this series, we\u2019ll talk about: Part 1: What even is a package, [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4\" \/>\n<meta property=\"og:site_name\" content=\"Salesforce Developers Blog\" \/>\n<meta property=\"article:published_time\" content=\"2018-06-26T18:00:42+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-11-05T09:23:31+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/d259t2jj6zp7qm.cloudfront.net\/images\/v1530034177-trailhead_module_git_and_github_basics_f1rqxy.png\" \/>\n\t<meta property=\"og:image:width\" content=\"200\" \/>\n\t<meta property=\"og:image:height\" content=\"200\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Ren\u00e9 Winkelmeyer\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@muenzpraeger\" \/>\n<meta name=\"twitter:site\" content=\"@SalesforceDevs\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Ren\u00e9 Winkelmeyer\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4\",\"url\":\"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4\",\"name\":\"Working with Modular Development and Unlocked Packages: Part 4 - Salesforce Developers Blog\",\"isPartOf\":{\"@id\":\"https:\/\/developer.salesforce.com\/blogs\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4#primaryimage\"},\"image\":{\"@id\":\"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4#primaryimage\"},\"thumbnailUrl\":\"https:\/\/d259t2jj6zp7qm.cloudfront.net\/images\/v1530034177-trailhead_module_git_and_github_basics_f1rqxy.png\",\"datePublished\":\"2018-06-26T18:00:42+00:00\",\"dateModified\":\"2025-11-05T09:23:31+00:00\",\"author\":{\"@id\":\"https:\/\/developer.salesforce.com\/blogs\/#\/schema\/person\/39898e09667c18948ea14200a398c1d0\"},\"breadcrumb\":{\"@id\":\"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4#primaryimage\",\"url\":\"https:\/\/d259t2jj6zp7qm.cloudfront.net\/images\/v1530034177-trailhead_module_git_and_github_basics_f1rqxy.png\",\"contentUrl\":\"https:\/\/d259t2jj6zp7qm.cloudfront.net\/images\/v1530034177-trailhead_module_git_and_github_basics_f1rqxy.png\",\"width\":\"200\",\"height\":\"200\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/developer.salesforce.com\/blogs\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Working with Modular Development and Unlocked Packages: Part 4\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/developer.salesforce.com\/blogs\/#website\",\"url\":\"https:\/\/developer.salesforce.com\/blogs\/\",\"name\":\"Salesforce Developers Blog\",\"description\":\"Elevating developer skills and connecting with the Salesforce Developers community\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/developer.salesforce.com\/blogs\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/developer.salesforce.com\/blogs\/#\/schema\/person\/39898e09667c18948ea14200a398c1d0\",\"name\":\"Ren\u00e9 Winkelmeyer\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/developer.salesforce.com\/blogs\/#\/schema\/person\/image\/5553df8fa142c10aef2f8ae461fdf88c\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/9a703ac9d630a79d2e9ed3906c39e1da7bc89a5b1d3b6ce4218d58c17db49c63?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/9a703ac9d630a79d2e9ed3906c39e1da7bc89a5b1d3b6ce4218d58c17db49c63?s=96&d=mm&r=g\",\"caption\":\"Ren\u00e9 Winkelmeyer\"},\"description\":\"Ren\u00e9 Winkelmeyer works as an Architect, Developer Relations, at Salesforce. He focuses on enterprise integrations, JavaScript, node, and all the other cool stuff that you can do with Salesforce technologies. You can follow him on Twitter @muenzpraeger or on GitHub @muenzpraeger.\",\"sameAs\":[\"https:\/\/blog.winkelmeyer.com\",\"https:\/\/www.linkedin.com\/in\/muenzpraeger\/\",\"https:\/\/x.com\/muenzpraeger\",\"https:\/\/www.youtube.com\/channel\/uch60rray2gi9m62z1loljca\"],\"url\":\"https:\/\/developer.salesforce.com\/blogs\/author\/rwinkelmeyer\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Working with Modular Development and Unlocked Packages: Part 4 - Salesforce Developers Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4","og_locale":"en_US","og_type":"article","og_title":"Working with Modular Development and Unlocked Packages: Part 4","og_description":"This is the fourth installment in a series exploring how to begin working with your apps in modular pieces, incorporating packages into your app development lifecycle, and what packaging may mean for your team\u2019s change management and release processes. Over the course of this series, we\u2019ll talk about: Part 1: What even is a package, [&hellip;]","og_url":"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4","og_site_name":"Salesforce Developers Blog","article_published_time":"2018-06-26T18:00:42+00:00","article_modified_time":"2025-11-05T09:23:31+00:00","og_image":[{"width":200,"height":200,"url":"https:\/\/d259t2jj6zp7qm.cloudfront.net\/images\/v1530034177-trailhead_module_git_and_github_basics_f1rqxy.png","type":"image\/png"}],"author":"Ren\u00e9 Winkelmeyer","twitter_card":"summary_large_image","twitter_creator":"@muenzpraeger","twitter_site":"@SalesforceDevs","twitter_misc":{"Written by":"Ren\u00e9 Winkelmeyer","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4","url":"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4","name":"Working with Modular Development and Unlocked Packages: Part 4 - Salesforce Developers Blog","isPartOf":{"@id":"https:\/\/developer.salesforce.com\/blogs\/#website"},"primaryImageOfPage":{"@id":"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4#primaryimage"},"image":{"@id":"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4#primaryimage"},"thumbnailUrl":"https:\/\/d259t2jj6zp7qm.cloudfront.net\/images\/v1530034177-trailhead_module_git_and_github_basics_f1rqxy.png","datePublished":"2018-06-26T18:00:42+00:00","dateModified":"2025-11-05T09:23:31+00:00","author":{"@id":"https:\/\/developer.salesforce.com\/blogs\/#\/schema\/person\/39898e09667c18948ea14200a398c1d0"},"breadcrumb":{"@id":"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4#primaryimage","url":"https:\/\/d259t2jj6zp7qm.cloudfront.net\/images\/v1530034177-trailhead_module_git_and_github_basics_f1rqxy.png","contentUrl":"https:\/\/d259t2jj6zp7qm.cloudfront.net\/images\/v1530034177-trailhead_module_git_and_github_basics_f1rqxy.png","width":"200","height":"200"},{"@type":"BreadcrumbList","@id":"https:\/\/developer.salesforce.com\/blogs\/2018\/06\/working-with-modular-development-and-unlocked-packages-part-4#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/developer.salesforce.com\/blogs"},{"@type":"ListItem","position":2,"name":"Working with Modular Development and Unlocked Packages: Part 4"}]},{"@type":"WebSite","@id":"https:\/\/developer.salesforce.com\/blogs\/#website","url":"https:\/\/developer.salesforce.com\/blogs\/","name":"Salesforce Developers Blog","description":"Elevating developer skills and connecting with the Salesforce Developers community","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/developer.salesforce.com\/blogs\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/developer.salesforce.com\/blogs\/#\/schema\/person\/39898e09667c18948ea14200a398c1d0","name":"Ren\u00e9 Winkelmeyer","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/developer.salesforce.com\/blogs\/#\/schema\/person\/image\/5553df8fa142c10aef2f8ae461fdf88c","url":"https:\/\/secure.gravatar.com\/avatar\/9a703ac9d630a79d2e9ed3906c39e1da7bc89a5b1d3b6ce4218d58c17db49c63?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/9a703ac9d630a79d2e9ed3906c39e1da7bc89a5b1d3b6ce4218d58c17db49c63?s=96&d=mm&r=g","caption":"Ren\u00e9 Winkelmeyer"},"description":"Ren\u00e9 Winkelmeyer works as an Architect, Developer Relations, at Salesforce. He focuses on enterprise integrations, JavaScript, node, and all the other cool stuff that you can do with Salesforce technologies. You can follow him on Twitter @muenzpraeger or on GitHub @muenzpraeger.","sameAs":["https:\/\/blog.winkelmeyer.com","https:\/\/www.linkedin.com\/in\/muenzpraeger\/","https:\/\/x.com\/muenzpraeger","https:\/\/www.youtube.com\/channel\/uch60rray2gi9m62z1loljca"],"url":"https:\/\/developer.salesforce.com\/blogs\/author\/rwinkelmeyer"}]}},"jetpack_featured_media_url":"https:\/\/d259t2jj6zp7qm.cloudfront.net\/images\/v1530034177-trailhead_module_git_and_github_basics_f1rqxy.png","authors":[{"name":"Ren\u00e9 Winkelmeyer","image_src":"https:\/\/secure.gravatar.com\/avatar\/9a703ac9d630a79d2e9ed3906c39e1da7bc89a5b1d3b6ce4218d58c17db49c63?s=24&d=mm&r=g"}],"_links":{"self":[{"href":"https:\/\/developer.salesforce.com\/blogs\/wp-json\/wp\/v2\/posts\/189254","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/developer.salesforce.com\/blogs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/developer.salesforce.com\/blogs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/developer.salesforce.com\/blogs\/wp-json\/wp\/v2\/users\/3071"}],"replies":[{"embeddable":true,"href":"https:\/\/developer.salesforce.com\/blogs\/wp-json\/wp\/v2\/comments?post=189254"}],"version-history":[{"count":6,"href":"https:\/\/developer.salesforce.com\/blogs\/wp-json\/wp\/v2\/posts\/189254\/revisions"}],"predecessor-version":[{"id":190449,"href":"https:\/\/developer.salesforce.com\/blogs\/wp-json\/wp\/v2\/posts\/189254\/revisions\/190449"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/developer.salesforce.com\/blogs\/wp-json\/wp\/v2\/media\/189260"}],"wp:attachment":[{"href":"https:\/\/developer.salesforce.com\/blogs\/wp-json\/wp\/v2\/media?parent=189254"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/developer.salesforce.com\/blogs\/wp-json\/wp\/v2\/categories?post=189254"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/developer.salesforce.com\/blogs\/wp-json\/wp\/v2\/tags?post=189254"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/developer.salesforce.com\/blogs\/wp-json\/wp\/v2\/coauthors?post=189254"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}