Merge branch 'master' into content/spring-boot

content/spring-boot
Kamran Ahmed 2 years ago committed by GitHub
commit 83504f495b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. BIN
      public/images/devops-ebook.png
  2. 28
      src/components/Analytics/Analytics.astro
  3. 14
      src/components/DownloadPopup.astro
  4. 2
      src/components/FAQs/Answer.astro
  5. 2
      src/components/FAQs/FAQs.astro
  6. 4
      src/components/FAQs/Question.astro
  7. 18
      src/components/Footer.astro
  8. 39
      src/components/InteractiveRoadmap/InteractiveRoadmap.astro
  9. 407
      src/components/InteractiveRoadmap/topic.js
  10. 20
      src/components/Navigation.astro
  11. 2
      src/components/OpenSourceBanner.astro
  12. 3
      src/components/Popup/Popup.astro
  13. 3
      src/components/Popup/popup.js
  14. 8
      src/components/ResourcesAlert.astro
  15. 79
      src/components/RoadmapHeader.astro
  16. 22
      src/components/Sponsor/Sponsor.astro
  17. 4
      src/components/SubscribePopup.astro
  18. 86
      src/components/TopicOverlay.astro
  19. 39
      src/components/YouTubeBanner.astro
  20. 5
      src/layouts/BaseLayout.astro
  21. 140
      src/roadmaps/backend/faqs.astro
  22. 10
      src/roadmaps/devops/devops.md
  23. 27
      src/roadmaps/frontend/faqs.astro
  24. 0
      src/roadmaps/python/content/100-python-roadmap-note.md
  25. 2
      src/roadmaps/software-design-architecture/software-design-architecture.md
  26. 34
      src/roadmaps/spring-boot/content/100-spring-core/100-introduction/100-terminology.md

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

@ -2,7 +2,8 @@
---
<script src='./analytics.js'></script>
<script async src='https://www.googletagmanager.com/gtag/js?id=UA-139582634-1'></script>
<script async src='https://www.googletagmanager.com/gtag/js?id=UA-139582634-1'
></script>
<script is:inline>
// @ts-nocheck
window.dataLayer = window.dataLayer || [];
@ -12,4 +13,29 @@
gtag('js', new Date());
gtag('config', 'UA-139582634-1');
document.addEventListener('click', (e) => {
let trackEl = e.target;
if (!trackEl.getAttribute('ga-category')) {
trackEl = trackEl.closest('[ga-category]');
}
if (!trackEl) {
return;
}
const category = trackEl.getAttribute('ga-category');
const action = trackEl.getAttribute('ga-action');
const label = trackEl.getAttribute('ga-label');
if (!category) {
return;
}
window.fireEvent({
category,
action,
label,
});
});
</script>

@ -34,9 +34,21 @@ import CaptchaFields from './Captcha/CaptchaFields.astro';
type='submit'
name='submit'
class='text-white bg-gradient-to-r from-amber-700 to-blue-800 hover:from-amber-800 hover:to-blue-900 font-regular rounded-md text-md px-5 py-2.5 w-full text-center mr-2'
onclick="window.fireEvent({ category: 'Subscription', action: 'Submitted Popup Form', label: 'Download Roadmap Popup' })"
submit-download-form
>
Send Link
</button>
</form>
</Popup>
<script>
document
.querySelector('[submit-download-form]')
?.addEventListener('click', () => {
window.fireEvent({
category: 'Subscription',
action: 'Submitted Popup Form',
label: 'Download Roadmap Popup',
});
});
</script>

@ -1,3 +1,3 @@
<div class='leading-relaxed text-left p-4 text-md text-gray-800 border-t border-t-gray-300 bg-gray-100 rounded-bl-md rounded-br-md'>
<div class='text-sm sm:text-base leading-relaxed text-left p-2 sm:p-4 text-md text-gray-800 border-t border-t-gray-300 bg-gray-100 rounded-bl-md rounded-br-md'>
<slot />
</div>

@ -1,7 +1,7 @@
<div class='border-t bg-gray-100'>
<div class='container'>
<div class='flex justify-between relative -top-5'>
<h1 class='text-md font-medium py-1 px-3 border bg-white rounded-md'>
<h1 class='text-sm sm:text-base font-medium py-1 px-3 border bg-white rounded-md'>
Frequently Asked Questions
</h1>
</div>

@ -14,9 +14,9 @@ const { question, isActive = false } = Astro.props;
>
<button
faq-question
class='flex flex-row justify-between items-center p-3 w-full'
class='flex flex-row justify-between items-center p-2 sm:p-3 w-full'
>
<span class='text-md text-left font-medium'>{question}</span>
<span class='text-sm sm:text-base text-left font-medium'>{question}</span>
<Icon icon='down' class='h-6 hidden sm:block text-gray-400' />
</button>
<div class:list={['answer', { hidden: !isActive }]} faq-answer>

@ -84,23 +84,29 @@ import Icon from './Icon.astro';
<div class='text-gray-400 text-sm'>
<p>
<a
href='https://thenewstack.io/category/devops/'
href='https://thenewstack.io/category/devops?utm_source=roadmap.sh&utm_medium=Referral&utm_campaign=Footer'
target='_blank'
onclick="window.fireEvent({ category: 'PartnerClick', action: `TNS Referral`, label: `TNS Referral - Footer` })"
ga-category='PartnerClick'
ga-action='TNS Referral'
ga-label='TNS Referral - Footer'
class='text-gray-400 hover:text-white'>DevOps</a
>
<span class='mx-1.5'>&middot;</span>
<a
href='https://thenewstack.io/category/kubernetes/'
href='https://thenewstack.io/category/kubernetes?utm_source=roadmap.sh&utm_medium=Referral&utm_campaign=Footer'
target='_blank'
onclick="window.fireEvent({ category: 'PartnerClick', action: `TNS Referral`, label: `TNS Referral - Footer` })"
ga-category='PartnerClick'
ga-action='TNS Referral'
ga-label='TNS Referral - Footer'
class='text-gray-400 hover:text-white'>Kubernetes</a
>
<span class='mx-1.5'>&middot;</span>
<a
href='https://thenewstack.io/category/cloud-native/'
href='https://thenewstack.io/category/cloud-native?utm_source=roadmap.sh&utm_medium=Referral&utm_campaign=Footer'
target='_blank'
onclick="window.fireEvent({ category: 'PartnerClick', action: `TNS Referral`, label: `TNS Referral - Footer` })"
ga-category='PartnerClick'
ga-action='TNS Referral'
ga-label='TNS Referral - Footer'
class='text-gray-400 hover:text-white'>Cloud-Native</a
>
</p>

@ -1,10 +1,10 @@
---
import DownloadPopup from "../DownloadPopup.astro";
import Loader from "../Loader.astro";
import ShareIcons from "../ShareIcons.astro";
import SubscribePopup from "../SubscribePopup.astro";
import TopicOverlay from "../TopicOverlay.astro";
import "./InteractiveRoadmap.css";
import DownloadPopup from '../DownloadPopup.astro';
import Loader from '../Loader.astro';
import ShareIcons from '../ShareIcons.astro';
import SubscribePopup from '../SubscribePopup.astro';
import TopicOverlay from '../TopicOverlay.astro';
import './InteractiveRoadmap.css';
export interface Props {
roadmapId: string;
@ -16,31 +16,32 @@ export interface Props {
};
}
const { roadmapId, jsonUrl, dimensions = null, description } =
Astro.props;
const { roadmapId, jsonUrl, dimensions = null, description } = Astro.props;
---
<link
rel="preload"
href="/fonts/balsamiq.woff2"
as="font"
type="font/woff2"
rel='preload'
href='/fonts/balsamiq.woff2'
as='font'
type='font/woff2'
crossorigin
slot="after-header"
slot='after-header'
/>
<div class="bg-gray-50 py-4 sm:py-12">
<div class="max-w-[1000px] container relative">
<div class='bg-gray-50 py-4 sm:py-12'>
<div class='max-w-[1000px] container relative'>
<ShareIcons
description={description}
pageUrl={`https://roadmap.sh/${roadmapId}`}
/>
<DownloadPopup />
<SubscribePopup />
<TopicOverlay />
<TopicOverlay roadmapId={roadmapId} />
<div
id="roadmap-svg"
style={dimensions ? `--aspect-ratio:${dimensions.width}/${dimensions.height}` : null}
id='roadmap-svg'
style={dimensions
? `--aspect-ratio:${dimensions.width}/${dimensions.height}`
: null}
data-roadmap-id={roadmapId}
data-json-url={jsonUrl}
>
@ -49,4 +50,4 @@ const { roadmapId, jsonUrl, dimensions = null, description } =
</div>
</div>
<script src="./roadmap.js"></script>
<script src='./roadmap.js'></script>

@ -1,204 +1,219 @@
export class Topic {
constructor() {
this.overlayId = 'topic-overlay';
this.contentId = 'topic-content';
this.loaderId = 'topic-loader';
this.topicBodyId = 'topic-body';
this.topicActionsId = 'topic-actions';
this.markTopicDoneId = 'mark-topic-done';
this.markTopicPendingId = 'mark-topic-pending';
this.closeTopicId = 'close-topic';
this.activeRoadmapId = null;
this.activeTopicId = null;
this.handleTopicClick = this.handleTopicClick.bind(this);
this.close = this.close.bind(this);
this.resetDOM = this.resetDOM.bind(this);
this.populate = this.populate.bind(this);
this.handleOverlayClick = this.handleOverlayClick.bind(this);
this.markAsDone = this.markAsDone.bind(this);
this.markAsPending = this.markAsPending.bind(this);
this.queryRoadmapElementsByTopicId = this.queryRoadmapElementsByTopicId.bind(this);
this.init = this.init.bind(this);
}
get loaderEl() {
return document.getElementById(this.loaderId);
}
get markTopicDoneEl() {
return document.getElementById(this.markTopicDoneId);
}
get markTopicPendingEl() {
return document.getElementById(this.markTopicPendingId);
}
get topicActionsEl() {
return document.getElementById(this.topicActionsId);
}
get contentEl() {
return document.getElementById(this.contentId);
}
get overlayEl() {
return document.getElementById(this.overlayId);
}
resetDOM(hideOverlay = false) {
if (hideOverlay) {
this.overlayEl.classList.add('hidden');
} else {
this.overlayEl.classList.remove('hidden');
}
this.loaderEl.classList.remove('hidden'); // Show loader
this.topicActionsEl.classList.add('hidden'); // Hide Actions
this.contentEl.replaceChildren(''); // Remove content
}
close() {
this.resetDOM(true);
this.activeRoadmapId = null;
this.activeTopicId = null;
}
/**
* @param {string | HTMLElement} html
*/
populate(html) {
this.contentEl.replaceChildren(html);
this.loaderEl.classList.add('hidden');
this.topicActionsEl.classList.remove('hidden');
const normalizedGroup = (this.activeTopicId || '').replace(/^\d+-/, '');
const isDone = localStorage.getItem(normalizedGroup) === 'done';
if (isDone) {
this.markTopicDoneEl.classList.add('hidden');
this.markTopicPendingEl.classList.remove('hidden');
} else {
this.markTopicDoneEl.classList.remove('hidden');
this.markTopicPendingEl.classList.add('hidden');
}
}
fetchTopicHtml(roadmapId, topicId) {
const topicPartial = topicId.replace(/^\d+-/, '').replaceAll(/:/g, '/');
const fullUrl = `/${roadmapId}/${topicPartial}/`;
return fetch(fullUrl)
.then((res) => {
return res.text();
})
.then((topicHtml) => {
// It's full HTML with page body, head etc.
// We only need the inner HTML of the #main-content
const node = new DOMParser().parseFromString(topicHtml, 'text/html');
return node.getElementById('main-content');
});
}
handleTopicClick(e) {
const { roadmapId, topicId } = e.detail;
if (!topicId || !roadmapId) {
console.log('Missing topic or roadmap: ', e.detail);
return;
}
this.activeRoadmapId = roadmapId;
this.activeTopicId = topicId;
if (/^ext_link/.test(topicId)) {
window.open(`https://${topicId.replace('ext_link:', '')}`);
return;
}
this.resetDOM();
this.fetchTopicHtml(roadmapId, topicId)
.then((content) => {
this.populate(content);
})
.catch((e) => {
console.error(e);
this.populate('Error loading the content!');
});
}
queryRoadmapElementsByTopicId(topicId) {
const elements = document.querySelectorAll(`[data-group-id$="-${topicId}"]`);
const matchingElements = [];
elements.forEach((element) => {
const foundGroupId = element?.dataset?.groupId || '';
const validGroupRegex = new RegExp(`^\\d+-${topicId}$`);
if (validGroupRegex.test(foundGroupId)) {
matchingElements.push(element);
}
});
return matchingElements;
constructor() {
this.overlayId = 'topic-overlay';
this.contentId = 'topic-content';
this.loaderId = 'topic-loader';
this.topicBodyId = 'topic-body';
this.topicActionsId = 'topic-actions';
this.markTopicDoneId = 'mark-topic-done';
this.markTopicPendingId = 'mark-topic-pending';
this.closeTopicId = 'close-topic';
this.contributionTextId = 'contrib-meta';
this.activeRoadmapId = null;
this.activeTopicId = null;
this.handleTopicClick = this.handleTopicClick.bind(this);
this.close = this.close.bind(this);
this.resetDOM = this.resetDOM.bind(this);
this.populate = this.populate.bind(this);
this.handleOverlayClick = this.handleOverlayClick.bind(this);
this.markAsDone = this.markAsDone.bind(this);
this.markAsPending = this.markAsPending.bind(this);
this.queryRoadmapElementsByTopicId =
this.queryRoadmapElementsByTopicId.bind(this);
this.init = this.init.bind(this);
}
get loaderEl() {
return document.getElementById(this.loaderId);
}
get markTopicDoneEl() {
return document.getElementById(this.markTopicDoneId);
}
get markTopicPendingEl() {
return document.getElementById(this.markTopicPendingId);
}
get topicActionsEl() {
return document.getElementById(this.topicActionsId);
}
get contributionTextEl() {
return document.getElementById(this.contributionTextId);
}
get contentEl() {
return document.getElementById(this.contentId);
}
get overlayEl() {
return document.getElementById(this.overlayId);
}
resetDOM(hideOverlay = false) {
if (hideOverlay) {
this.overlayEl.classList.add('hidden');
} else {
this.overlayEl.classList.remove('hidden');
}
this.loaderEl.classList.remove('hidden'); // Show loader
this.topicActionsEl.classList.add('hidden'); // Hide Actions
this.contributionTextEl.classList.add('hidden'); // Hide contribution text
this.contentEl.replaceChildren(''); // Remove content
}
close() {
this.resetDOM(true);
this.activeRoadmapId = null;
this.activeTopicId = null;
}
/**
* @param {string | HTMLElement} html
*/
populate(html) {
this.contentEl.replaceChildren(html);
this.loaderEl.classList.add('hidden');
this.topicActionsEl.classList.remove('hidden');
this.contributionTextEl.classList.remove('hidden');
const normalizedGroup = (this.activeTopicId || '').replace(/^\d+-/, '');
const isDone = localStorage.getItem(normalizedGroup) === 'done';
if (isDone) {
this.markTopicDoneEl.classList.add('hidden');
this.markTopicPendingEl.classList.remove('hidden');
} else {
this.markTopicDoneEl.classList.remove('hidden');
this.markTopicPendingEl.classList.add('hidden');
}
markAsDone(topicId) {
const updatedTopicId = topicId.replace(/^\d+-/, '');
localStorage.setItem(updatedTopicId, 'done');
this.queryRoadmapElementsByTopicId(updatedTopicId).forEach((item) => {
item?.classList?.add('done');
}
fetchTopicHtml(roadmapId, topicId) {
const topicPartial = topicId.replace(/^\d+-/, '').replaceAll(/:/g, '/');
const fullUrl = `/${roadmapId}/${topicPartial}/`;
return fetch(fullUrl)
.then((res) => {
return res.text();
})
.then((topicHtml) => {
// It's full HTML with page body, head etc.
// We only need the inner HTML of the #main-content
const node = new DOMParser().parseFromString(topicHtml, 'text/html');
return node.getElementById('main-content');
});
}
markAsPending(topicId) {
const updatedTopicId = topicId.replace(/^\d+-/, '');
localStorage.removeItem(updatedTopicId);
this.queryRoadmapElementsByTopicId(updatedTopicId).forEach((item) => {
item?.classList?.remove('done');
}
handleTopicClick(e) {
const { roadmapId, topicId } = e.detail;
if (!topicId || !roadmapId) {
console.log('Missing topic or roadmap: ', e.detail);
return;
}
this.activeRoadmapId = roadmapId;
this.activeTopicId = topicId;
if (/^ext_link/.test(topicId)) {
window.open(`https://${topicId.replace('ext_link:', '')}`);
return;
}
this.resetDOM();
this.fetchTopicHtml(roadmapId, topicId)
.then((content) => {
this.populate(content);
})
.catch((e) => {
console.error(e);
this.populate('Error loading the content!');
});
}
handleOverlayClick(e) {
const isClickedInsideTopic = e.target.closest(`#${this.topicBodyId}`);
if (!isClickedInsideTopic) {
this.close();
return;
}
const isClickedDone = e.target.id === this.markTopicDoneId || e.target.closest(`#${this.markTopicDoneId}`);
if (isClickedDone) {
this.markAsDone(this.activeTopicId);
this.close();
}
const isClickedPending = e.target.id === this.markTopicPendingId || e.target.closest(`#${this.markTopicPendingId}`);
if (isClickedPending) {
this.markAsPending(this.activeTopicId);
this.close();
}
queryRoadmapElementsByTopicId(topicId) {
const elements = document.querySelectorAll(
`[data-group-id$="-${topicId}"]`
);
const matchingElements = [];
elements.forEach((element) => {
const foundGroupId = element?.dataset?.groupId || '';
const validGroupRegex = new RegExp(`^\\d+-${topicId}$`);
if (validGroupRegex.test(foundGroupId)) {
matchingElements.push(element);
}
const isClickedClose = e.target.id === this.closeTopicId || e.target.closest(`#${this.closeTopicId}`);
if (isClickedClose) {
});
return matchingElements;
}
markAsDone(topicId) {
const updatedTopicId = topicId.replace(/^\d+-/, '');
localStorage.setItem(updatedTopicId, 'done');
this.queryRoadmapElementsByTopicId(updatedTopicId).forEach((item) => {
item?.classList?.add('done');
});
}
markAsPending(topicId) {
const updatedTopicId = topicId.replace(/^\d+-/, '');
localStorage.removeItem(updatedTopicId);
this.queryRoadmapElementsByTopicId(updatedTopicId).forEach((item) => {
item?.classList?.remove('done');
});
}
handleOverlayClick(e) {
const isClickedInsideTopic = e.target.closest(`#${this.topicBodyId}`);
if (!isClickedInsideTopic) {
this.close();
return;
}
const isClickedDone =
e.target.id === this.markTopicDoneId ||
e.target.closest(`#${this.markTopicDoneId}`);
if (isClickedDone) {
this.markAsDone(this.activeTopicId);
this.close();
}
const isClickedPending =
e.target.id === this.markTopicPendingId ||
e.target.closest(`#${this.markTopicPendingId}`);
if (isClickedPending) {
this.markAsPending(this.activeTopicId);
this.close();
}
const isClickedClose =
e.target.id === this.closeTopicId ||
e.target.closest(`#${this.closeTopicId}`);
if (isClickedClose) {
this.close();
}
}
init() {
window.addEventListener('topic.click', this.handleTopicClick);
window.addEventListener('click', this.handleOverlayClick);
window.addEventListener('keydown', (e) => {
if (e.key.toLowerCase() === 'escape') {
this.close();
}
}
init() {
window.addEventListener('topic.click', this.handleTopicClick);
window.addEventListener('click', this.handleOverlayClick);
window.addEventListener('keydown', (e) => {
if (e.key.toLowerCase() === 'escape') {
this.close();
}
});
}
});
}
}

@ -32,24 +32,22 @@ import Icon from './Icon.astro';
<!-- Mobile Navigation Button -->
<button
id='show-mobile-navigation'
class='text-gray-400 hover:text-gray-50 block sm:hidden cursor-pointer'
aria-label='Menu'
onclick="document.getElementById('mobile-navigation').classList.remove('hidden');"
show-mobile-nav
>
<Icon icon='hamburger' />
</button>
<!-- Mobile Navigation Items -->
<div
id='mobile-navigation'
class='fixed top-0 bottom-0 left-0 right-0 z-40 bg-slate-900 items-center flex hidden'
mobile-nav
>
<button
id='close-mobile-navigation'
close-mobile-nav
class='text-gray-400 hover:text-gray-50 block cursor-pointer absolute top-6 right-6'
aria-label='Close Menu'
onclick="document.getElementById('mobile-navigation').classList.add('hidden');"
>
<Icon icon='close' />
</button>
@ -80,3 +78,15 @@ import Icon from './Icon.astro';
</div>
</nav>
</div>
<script>
document.querySelector('[show-mobile-nav]')?.addEventListener('click', () => {
document.querySelector('[mobile-nav]')?.classList.remove('hidden');
});
document
.querySelector('[close-mobile-nav]')
?.addEventListener('click', () => {
document.querySelector('[mobile-nav]')?.classList.add('hidden');
});
</script>

@ -7,7 +7,7 @@ const starCount = await getFormattedStars('kamranahmedse/developer-roadmap');
<div class='py-6 sm:py-16 border-b border-t text-left sm:text-center bg-white'>
<div class='max-w-[600px] container'>
<h2 class='text-3xl sm:text-5xl font-bold'>Open Source</h1>
<h2 class='text-2xl sm:text-5xl font-bold'>Open Source</h2>
<p class='text-gray-600 text-sm sm:text-lg leading-relaxed my-2.5 sm:my-5'>
The project is OpenSource, <a
href='https://github.com/search?o=desc&q=stars%3A%3E100000&s=stars&type=Repositories'

@ -21,8 +21,7 @@ const { id, title, subtitle } = Astro.props;
<div class='relative bg-white rounded-lg shadow popup-body'>
<button
type='button'
class='absolute top-3 right-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center'
onclick='this.closest(".popup").classList.add("hidden")'
class='absolute top-3 right-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center popup-close'
>
<Icon icon='close' />
<span class='sr-only'>Close popup</span>

@ -31,8 +31,9 @@ export class Popup {
const target = e.target;
const popupBody = target.closest('.popup-body');
const closestPopup = target.closest('.popup');
const closeBtn = target.closest('.popup-close');
if (popupBody) {
if (!closeBtn && popupBody) {
return;
}

@ -16,7 +16,7 @@ const roadmapTitle =
<div
class:list={[
'mt-4 sm:mt-7 border rounded-md mb-0',
'mt-4 sm:mt-7 border-0 sm:border rounded-md mb-0',
{
'sm:-mb-[82px]': hasTNSBanner,
'sm:-mb-[65px]': !hasTNSBanner,
@ -29,10 +29,12 @@ const roadmapTitle =
<p class='text-sm'>
Get the latest {roadmapTitle} news from our sister site{' '}
<a
href='https://thenewstack.io'
href='https://thenewstack.io?utm_source=roadmap.sh&utm_medium=Referral&utm_campaign=Alert'
target='_blank'
class='font-semibold underline'
onclick="window.fireEvent({ category: 'PartnerClick', action: `TNS Referral`, label: `TNS Referral - Roadmap` })"
ga-category='PartnerClick'
ga-action='TNS Referral'
ga-label='TNS Referral - Roadmap'
>
TheNewStack.io
</a>

@ -1,8 +1,8 @@
---
import Icon from "./Icon.astro";
import ResourcesAlert from "./ResourcesAlert.astro";
import TopicSearch from "./TopicSearch/TopicSearch.astro";
import YouTubeAlert from "./YouTubeAlert.astro";
import Icon from './Icon.astro';
import ResourcesAlert from './ResourcesAlert.astro';
import TopicSearch from './TopicSearch/TopicSearch.astro';
import YouTubeAlert from './YouTubeAlert.astro';
export interface Props {
title: string;
@ -22,50 +22,57 @@ const {
hasTopics = false,
} = Astro.props;
const isRoadmapReady = !isUpcoming;
---
<div class="border-b">
<div class="py-5 sm:py-12 container relative">
<div class='border-b'>
<div class='py-5 sm:py-12 container relative'>
<YouTubeAlert />
<div class="mt-0 mb-3 sm:mb-6 sm:mt-4">
<h1 class="text-2xl sm:text-4xl mb-0.5 sm:mb-2 font-bold">
<div class='mt-0 mb-3 sm:mb-6 sm:mt-4'>
<h1 class='text-2xl sm:text-4xl mb-0.5 sm:mb-2 font-bold'>
{title}
</h1>
<p class="text-gray-500 text-sm sm:text-lg">{description}</p>
<p class='text-gray-500 text-sm sm:text-lg'>{description}</p>
</div>
<div class="flex justify-between">
<div class="flex gap-1 sm:gap-2">
<div class='flex justify-between'>
<div class='flex gap-1 sm:gap-2'>
{
!hasSearch && (
<>
<a href='/roadmaps/' class='bg-gray-500 py-1.5 px-3 rounded-md text-white text-xs sm:text-sm font-medium hover:bg-gray-600' aria-label="Back to All Roadmaps">
&larr;<span class='hidden sm:inline'>&nbsp;All Roadmaps</span>
</a>
<a
href='/roadmaps/'
class='bg-gray-500 py-1.5 px-3 rounded-md text-white text-xs sm:text-sm font-medium hover:bg-gray-600'
aria-label='Back to All Roadmaps'
>
&larr;<span class='hidden sm:inline'>&nbsp;All Roadmaps</span>
</a>
{isRoadmapReady && (
<button
data-popup="download-popup"
class="inline-flex items-center justify-center bg-yellow-400 py-1.5 px-3 text-xs sm:text-sm font-medium rounded-md hover:bg-yellow-500"
aria-label="Download Roadmap"
onclick="window.fireEvent({ category: 'Subscription', action: 'Clicked Popup Opener', label: 'Download Roadmap Popup' })"
data-popup='download-popup'
class='inline-flex items-center justify-center bg-yellow-400 py-1.5 px-3 text-xs sm:text-sm font-medium rounded-md hover:bg-yellow-500'
aria-label='Download Roadmap'
ga-category='Subscription'
ga-action='Clicked Popup Opener'
ga-label='Download Roadmap Popup'
>
<Icon icon="download" />
<span class="hidden sm:inline ml-2">Download</span>
<Icon icon='download' />
<span class='hidden sm:inline ml-2'>Download</span>
</button>
)}
<button
data-popup="subscribe-popup"
class="inline-flex items-center justify-center bg-yellow-400 py-1.5 px-3 text-xs sm:text-sm font-medium rounded-md hover:bg-yellow-500"
aria-label="Subscribe for Updates"
onclick="window.fireEvent({ category: 'Subscription', action: 'Clicked Popup Opener', label: 'Subscribe Roadmap Popup' })"
data-popup='subscribe-popup'
class='inline-flex items-center justify-center bg-yellow-400 py-1.5 px-3 text-xs sm:text-sm font-medium rounded-md hover:bg-yellow-500'
aria-label='Subscribe for Updates'
ga-category='Subscription'
ga-action='Clicked Popup Opener'
ga-label='Subscribe Roadmap Popup'
>
<Icon icon="email" />
<span class="ml-2">Subscribe</span>
<Icon icon='email' />
<span class='ml-2'>Subscribe</span>
</button>
</>
)
@ -75,11 +82,11 @@ const isRoadmapReady = !isUpcoming;
hasSearch && (
<a
href={`/${roadmapId}/`}
class="bg-gray-500 py-1.5 px-3 rounded-md text-white text-xs sm:text-sm font-medium hover:bg-gray-600"
aria-label="Back to Visual Roadmap"
class='bg-gray-500 py-1.5 px-3 rounded-md text-white text-xs sm:text-sm font-medium hover:bg-gray-600'
aria-label='Back to Visual Roadmap'
>
&larr;
<span class="inline">&nbsp;Visual Roadmap</span>
<span class='inline'>&nbsp;Visual Roadmap</span>
</a>
)
}
@ -89,13 +96,13 @@ const isRoadmapReady = !isUpcoming;
isRoadmapReady && (
<a
href={`https://github.com/kamranahmedse/developer-roadmap/issues/new?title=[Suggestion] ${title}`}
target="_blank"
class="inline-flex items-center justify-center bg-gray-500 text-white py-1.5 px-3 text-xs sm:text-sm font-medium rounded-md hover:bg-gray-600"
aria-label="Suggest Changes"
target='_blank'
class='inline-flex items-center justify-center bg-gray-500 text-white py-1.5 px-3 text-xs sm:text-sm font-medium rounded-md hover:bg-gray-600'
aria-label='Suggest Changes'
>
<Icon icon="comment" class="h-3 w-3" />
<span class="ml-2 hidden sm:inline">Suggest Changes</span>
<span class="ml-2 inline sm:hidden">Suggest</span>
<Icon icon='comment' class='h-3 w-3' />
<span class='ml-2 hidden sm:inline'>Suggest Changes</span>
<span class='ml-2 inline sm:hidden'>Suggest</span>
</a>
)
}

@ -1,5 +1,6 @@
---
import type { GAEventType } from '../Analytics/analytics';
import Icon from '../Icon.astro';
export type SponsorType = {
url: string;
@ -25,10 +26,19 @@ const {
id='sponsor-ad'
target='_blank'
rel='noopener sponsored'
onclick={event ? `window.fireEvent(${JSON.stringify(event)})` : ''}
ga-category={event?.category}
ga-action={event?.action}
ga-label={event?.label}
class='fixed bottom-[15px] right-[20px] outline-transparent z-50 bg-white max-w-[330px] shadow-lg outline-0 hidden'
>
<img src={imageUrl} class='w-[100px] lg:w-[130px]' alt='Sponsor Banner' />
<button
class='absolute top-1.5 right-1.5 text-gray-300 hover:text-gray-800'
aria-label='Close'
close-sponsor
>
<Icon icon='close' class='h-4' />
</button>
<img src={imageUrl} class='h-[150px] lg:h-[169px]' alt='Sponsor Banner' />
<span class='text-sm flex flex-col justify-between'>
<span class='p-[10px]'>
<span class='font-semibold mb-0.5 block'>{title}</span>
@ -37,3 +47,11 @@ const {
<span class='sponsor-footer'>Partner Content</span>
</span>
</a>
<script>
document.querySelector('[close-sponsor]')?.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
document.getElementById('sponsor-ad')?.classList.add('hidden');
});
</script>

@ -33,7 +33,9 @@ import CaptchaFields from './Captcha/CaptchaFields.astro';
type='submit'
name='submit'
class='text-white bg-gradient-to-r from-amber-700 to-blue-800 hover:from-amber-800 hover:to-blue-900 font-regular rounded-md text-md px-5 py-2.5 w-full text-center mr-2'
onclick="window.fireEvent({ category: 'Subscription', action: 'Submitted Popup Form', label: 'Subscribe Roadmap Popup' })"
ga-category='Subscription'
ga-action='Submitted Popup Form'
ga-label='Subscribe Roadmap Popup'
>
Subscribe
</button>

@ -1,31 +1,69 @@
---
import Icon from "./Icon.astro";
import Loader from "./Loader.astro";
import Icon from './Icon.astro';
import Loader from './Loader.astro';
export interface Props {
roadmapId: string;
}
const { roadmapId } = Astro.props;
const githubLink = `https://github.com/kamranahmedse/developer-roadmap/tree/master/src/roadmaps/${roadmapId}/content`;
---
<div id='topic-overlay' class='hidden'>
<div class="fixed top-0 right-0 z-40 h-screen p-4 sm:p-6 overflow-y-auto bg-white w-full sm:max-w-[600px]" tabindex="-1" id='topic-body'>
<div id='topic-loader' class='hidden'>
<Loader />
</div>
<div id='topic-actions' class='hidden mb-2'>
<button id='mark-topic-done' class='bg-green-600 text-white p-1 px-2 text-sm rounded-md hover:bg-green-700 inline-flex items-center'>
<Icon icon="check" /> <span class='ml-2'>Mark as Done</span>
</button>
<button id='mark-topic-pending' class='hidden bg-red-600 text-white p-1 px-2 text-sm rounded-md hover:bg-red-700 inline-flex items-center'>
<Icon icon="reset" /> <span class='ml-2'>Mark as Pending</span>
</button>
<button type="button" id='close-topic' class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 absolute top-2.5 right-2.5 inline-flex items-center">
<Icon icon="close" />
</button>
</div>
<div id='topic-content' class='prose prose-h1:mt-7 prose-h1:mb-2.5 prose-p:mt-0 prose-p:mb-2 prose-li:m-0 prose-li:mb-0.5 prose-h2:mb-3 prose-h2:mt-0'></div>
<div
class='fixed top-0 right-0 z-40 h-screen p-4 sm:p-6 overflow-y-auto bg-white w-full sm:max-w-[600px]'
tabindex='-1'
id='topic-body'
>
<div id='topic-loader' class='hidden'>
<Loader />
</div>
<div id='topic-actions' class='hidden mb-2'>
<button
id='mark-topic-done'
class='bg-green-600 text-white p-1 px-2 text-sm rounded-md hover:bg-green-700 inline-flex items-center'
>
<Icon icon='check' />
<span class='ml-2'>Mark as Done</span>
</button>
<button
id='mark-topic-pending'
class='hidden bg-red-600 text-white p-1 px-2 text-sm rounded-md hover:bg-red-700 inline-flex items-center'
>
<Icon icon='reset' />
<span class='ml-2'>Mark as Pending</span>
</button>
<button
type='button'
id='close-topic'
class='text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 absolute top-2.5 right-2.5 inline-flex items-center'
>
<Icon icon='close' />
</button>
</div>
<div class="bg-gray-900 bg-opacity-50 dark:bg-opacity-80 fixed inset-0 z-30"></div>
<div
id='topic-content'
class='prose prose-h1:mt-7 prose-h1:mb-2.5 prose-p:mt-0 prose-p:mb-2 prose-li:m-0 prose-li:mb-0.5 prose-h2:mb-3 prose-h2:mt-0'
>
</div>
<p
id='contrib-meta'
class='text-gray-400 text-sm border-t pt-3 mt-10 hidden'
>
We are still working on this page. You can contribute by submitting a
brief description and a few links to learn more about this topic <a
target='_blank'
class='underline text-blue-700'
href={githubLink}>on GitHub repository.</a
>.
</p>
</div>
<div class='bg-gray-900 bg-opacity-50 dark:bg-opacity-80 fixed inset-0 z-30'>
</div>
</div>

@ -1,12 +1,33 @@
---
import Icon from "./Icon.astro";
import Icon from './Icon.astro';
---
<div class='sticky top-0 border-b border-b-yellow-300 z-10 flex h-[37px]' id='sticky-youtube-banner'>
<a href='https://youtube.com/theroadmap?sub_confirmation=1' target='_blank' class='flex bg-yellow-200 text-center flex-1 items-center justify-center text-sm hover:bg-yellow-300 outline-0 '>
<Icon icon="youtube" class="mr-2" /> We now have a YouTube Channel.&nbsp;<span class='hidden sm:inline'>Subscribe for the video content.</span>
</a>
<button class='text-yellow-500 bg-yellow-200 hover:text-yellow-900 hover:bg-yellow-400 outline-0 px-2' onclick='this.parentElement.classList.add("hidden")' aria-label="Close">
<Icon icon="close" />
</button>
</div>
<div
class='sticky top-0 border-b border-b-yellow-300 z-10 flex h-[37px]'
youtube-banner
>
<a
href='https://youtube.com/theroadmap?sub_confirmation=1'
target='_blank'
class='flex bg-yellow-200 text-center flex-1 items-center justify-center text-sm hover:bg-yellow-300 outline-0'
>
<Icon icon='youtube' class='mr-2' /> We now have a YouTube Channel.&nbsp;<span
class='hidden sm:inline'>Subscribe for the video content.</span
>
</a>
<button
class='text-yellow-500 bg-yellow-200 hover:text-yellow-900 hover:bg-yellow-400 outline-0 px-2'
aria-label='Close'
close-youtube-banner
>
<Icon icon='close' />
</button>
</div>
<script>
document
.querySelector('[close-youtube-banner]')
?.addEventListener('click', () => {
document.querySelector('[youtube-banner]').remove();
});
</script>

@ -28,10 +28,7 @@ const {
} = Astro.props;
// Remove trailing slashes to consider the page as canonical
const currentPageAbsoluteUrl = `https://roadmap.sh${permalink.replace(
/\/$/,
''
)}`;
const currentPageAbsoluteUrl = `https://roadmap.sh${permalink}`;
const commitUrl = `https://github.com/kamranahmedse/developer-roadmap/commit/${
import.meta.env.GITHUB_SHA

@ -0,0 +1,140 @@
---
import Answer from '../../components/FAQs/Answer.astro';
import FAQs from '../../components/FAQs/FAQs.astro';
import Question from '../../components/FAQs/Question.astro';
const salaryLink =
'https://www.glassdoor.com/Salaries/united-states-backend-developer-salary-SRCH_IL.0,13_IN1_KO14,31.htm';
---
<FAQs>
<Question isActive question='What is Backend Development?'>
<Answer>
<p class='mb-3'>
Backend web development is the part of web development that deals with
the server-side of a web application. This includes creating and
managing the server-side logic, connecting the application to a
database, creating server-side APIs, handling user authentication and
authorization, and processing and responding to user requests. It often
involves the use of programming languages such as Python, Java, Ruby,
PHP, JavaScript (Node.js), and .NET languages.
</p>
<p>
A backend developer is responsible for the development of server-side
components of a web application i.e. working with databases, handling
requests, creating server-side APIs that can be consumed by frontend
developers to retrieve and manipulate data, ensuring the scalability of
the systems i.e. making sure that the backend can handle a high volume
of traffic and is performant, integrating external services like payment
gateways, message queues, cloud services, etc.
</p>
</Answer>
</Question>
<Question question='How to become a Backend Developer?'>
<Answer>
<p class='mb-3'>
If you are a complete beginner who is just getting started, you can
start by learning <span class='bg-yellow-100'
>a backend programming language</span
> such as Python, Ruby, Java, Go etc. Once you have got the basic to intermediate
understanding of the language, learn about <span class='bg-yellow-100'
>the package manager</span
> for that language and learn how to install and use external packages into
your applications. Learn the basics of some <span class='bg-yellow-100'
>relational database</span
> e.g. PostgreSQL and learn how to run simple CRUD operations. Optionally,
you can pick up and learn a <span class='bg-yellow-100'
>web framework</span
> for the language of your choice as well. Learn how to build a <span
class='bg-yellow-100'>simple RESTful API</span
> and implement simple Authentication/Authorization into it. While you are
learning all the items mentioned above, don't forget to learn about <span
class='bg-yellow-100'>Git and GitHub</span
> as well.
</p>
<p class='mb-3'>
After following all the instructions above, you can start applying for
the entry level backend developer jobs. Also, look at the backend
developer roadmap above to get an idea about the landscape and see what
else you are missing. A degree in computer science or related field is
not always necessary but networking, building a portfolio and actively
seeking internships, junior developer positions or consulting can help
to start and advance a career as a backend developer.
</p>
<p>
Note: remember to make a lot of projects while you are learning to
solidify your understanding of the concepts. Also, it's important to
have the attitude of continuous learning to improve your skills and be
prepared for the fast-paced technology evolution in the industry.
</p>
</Answer>
</Question>
<Question question='How long does it take to become a Backend Developer?'>
<Answer>
<p class='mb-3'>
The amount of time it takes to become a backend developer can vary
depending on several factors, such as your learning pace, previous
experience and the amount of time you are able to dedicate to learning.
</p>
<p>
If you have a background in computer science or a related field, and
have experience with programming, you may be able to become a backend
developer relatively quickly, potentially within a few months. However,
if you are starting with little or no prior experience or education in
computer science, it may take longer to develop the necessary skills and
gain the experience needed to be a proficient backend developer. It
could take anywhere from 6 months to 2 years.
</p>
</Answer>
</Question>
<Question question='What are the Backend Developer salaries?'>
<Answer>
<p class='mb-3'>
Backend developer salaries can vary depending on factors such as
location, experience, and company size. According to data from
Glassdoor, the average base salary for a backend developer in the United
States is around $92,000 per year. However, this number can vary greatly
depending on location, with the highest-paying cities such as San
Francisco, Seattle and New York having an average salary of $120,000 to
$135,000 per year.
</p>
<p class='mb-3'>
It's important to keep in mind that these are just averages, and
salaries can vary greatly depending on factors such as experience level,
specific skills, and the company you work for. With more experience and
specific skills relevant to the job you are applying for you can expect
to earn more.
</p>
<p>
It is worth looking at a range of resources, including salary surveys,
and job boards to get a general understanding of the current market in
your location and experience level. Also try reaching out to other
professionals in the field and getting an understanding of their own
experience and salary ranges.
</p>
</Answer>
</Question>
<Question question='Should I learn everything listed on the Backend Roadmap?'>
<Answer>
<p class='mb-3'>
This roadmap contains everything that you might encounter while working
as a Backend Developer. You may not need everything listed on this
roadmap to get into the industry; every job is different and most of the
jobs will require a subset of the items on the roadmap. However, knowing
what you don't know is as important as knowing things, so you can use
this roadmap to get an idea of what you are missing as well.
</p>
<p>
If you are a beginner who is just getting started, don't feel
overwhelmed by looking at this roadmap. Look at the answer to the FAQ
"How to become a Backend Developer?"
</p>
</Answer>
</Question>
</FAQs>

@ -11,14 +11,14 @@ dimensions:
width: 968
height: 2527.46
sponsor:
url: "https://www.getambassador.io/edge-stack-guide-v4?utm_source=roadmap.sh&utm_medium=ebook&utm_campaign=edgestack-guide"
url: "https://www.tigera.io/lp/kubernetes-security-and-observability-ebook/?utm_campaign=O%27Reilly-book-final-release&utm_medium=web&utm_source=Insight-Partners-popup"
title: "Free eBook"
imageUrl: "https://i.imgur.com/0bH1Vl6.png"
description: "Learn about API Gateways, Microservices, Load Balancing, and more with this free eBook."
imageUrl: "/images/devops-ebook.png"
description: "Learn how to secure and troubleshoot your cloud-native applications with this free eBook."
event:
category: "SponsorClick"
action: "Ambassador EBook Redirect"
label: "Clicked Ambassador EBook Link"
action: "Tigera EBook Redirect"
label: "Clicked Tigera EBook Link"
seo:
title: "DevOps Roadmap: Learn to become a DevOps Engineer or SRE"
description: "Community driven, articles, resources, guides, interview questions, quizzes for DevOps. Learn to become a modern DevOps engineer by following the steps, skills, resources and guides listed in this roadmap."

@ -11,7 +11,7 @@ const salaryLink =
<Question isActive question='What is Frontend Development?'>
<Answer>
<p class='mb-3'>
Front-end development is the devleopment of the visual and interactive
Front-end development is the devleopment of visual and interactive
elements of a website that users interact with directly. It's a
combination of HTML, CSS and JavaScript, where HTML provides the
structure, CSS the styling and layout, and JavaScript the dynamic
@ -77,20 +77,23 @@ const salaryLink =
</p>
</Answer>
</Question>
<Question question='What are the Frontend Developer Salaries?'>
<Question question='What are the Frontend Developer salaries?'>
<Answer>
<p class='mb-3'>
Frontend developer salaries can vary depending on factors such as
location, experience, and company size. According to data from the
website
<a
class='underline text-blue-700'
rel='nofollow'
target='_blank'
href={salaryLink}>Glassdoor</a
>, the average base salary for a frontend developer in the United States
is around $84,454 per year, although this can range from around $55,000
to $131,000 or more depending on the individual factors mentioned above.
location, experience, and company size. According to data from
Glassdoor, the average base salary for a frontend developer in the
United States is around $80,000 per year. However, this number can vary
greatly depending on location, with the highest-paying cities such as
San Francisco, Seattle, and New York having an average salary of
$110,000 to $130,000.
</p>
<p class='mb-3'>
It's important to keep in mind that these are just averages, and
salaries can vary greatly depending on factors such as experience level,
specific skills, and the company you work for. With more experience and
specific skills you can expect to earn more.
</p>
<p>

@ -2,7 +2,7 @@
jsonUrl: "/jsons/software-design-architecture.json"
pdfUrl: "/pdfs/software-design-architecture.pdf"
order: 11
featuredTitle: "Software Design"
featuredTitle: "Design and Architecture"
featuredDescription: "Guide to learn software design and architecture"
title: "Software Design and Architecture"
description: "Step by step guide to learn software design and architecture"

@ -1,18 +1,20 @@
# Terminology
Spring Boot is a framework for building Spring-based applications. It provides a set of features and tools to help developers quickly and easily create stand-alone, production-grade Spring-based applications.
Here are a few key terms and concepts that are commonly used when working with Spring Boot:
- Starters: Spring Boot starters are a set of convenient dependency descriptors that you can include in your application to add useful libraries and frameworks to your project. For example, if you want to add Spring MVC to your project, you can include the "spring-boot-starter-web" dependency in your pom.xml file, and Spring Boot will automatically add all of the necessary dependencies to your project.
- Auto-configuration: One of the key features of Spring Boot is its ability to automatically configure your application based on the dependencies that you have included in your project. This can include things like setting up an embedded web server, configuring a database connection, or enabling security features.
- Properties: Spring Boot applications can be configured using a variety of properties. These properties can be defined in a application.properties or application.yml file in the project classpath, and they can be used to configure things like the server port, database connection details, and various other aspects of the application.
- Spring Initializer: Spring Initializer is a web-based tool that allows you to quickly generate a basic structure for a new Spring Boot project, including a pom.xml file with the appropriate dependencies and a simple "Hello, World" application.
For more information visit the following links:
- [Spring Boot - Introduction](https://www.tutorialspoint.com/spring_boot/spring_boot_introduction.htm)
- [Spring Boot Tutorial](https://www.digitalocean.com/community/tutorials/spring-boot-tutorial)
Spring Core is the foundation of the Spring Framework, which provides a comprehensive programming and configuration model for Java-based applications. Here are some key terms and concepts related to Spring Core:
- Beans: In Spring, a "bean" is a Java object that is managed by the Spring container. Beans are typically defined using configuration metadata, which can be specified in XML, Java annotations, or Java code.
- Inversion of Control (IoC): One of the main principles of Spring is Inversion of Control (IoC), which means that the Spring container is responsible for managing the lifecycle of beans and injecting their dependencies.
- Dependency Injection (DI): Spring uses Dependency Injection (DI) to manage the dependencies between beans. In DI, an object's dependencies are provided to it by the container, rather than the object creating or looking up its own dependencies.
- Container: The Spring container is the core part of the Spring Framework, which creates and manages beans and their dependencies.
- ApplicationContext: An ApplicationContext is an implementation of the Spring container. It is responsible for loading and managing the configuration metadata and creating the beans defined in that metadata.
- Aspect-Oriented Programming (AOP): Spring supports Aspect-Oriented Programming (AOP), which allows you to separate cross-cutting concerns, such as logging or security, from the business logic of your application.
- Events: Spring provides an event model that allows beans to send and receive events. This is used to decouple the beans from each other, making the application more loosely coupled.
- ApplicationEvent and listener: Spring support publish subscribe model for event handling, ApplicationEvent defines event object, and the listener is a class that implements ApplicationListener interface, listening for the specific event and take the necessary action.
- Data Access: Spring provides a consistent, high-level abstraction for data access using various frameworks like JDBC, Hibernate, JPA.
- Transactions: Spring provides a flexible, consistent and easy way to declaratively manage transactions with different underlying technologies such as JPA, JDBC, and Hibernate.
- Task Execution and Scheduling: Spring provides a TaskExecutor and TaskScheduler, providing a convenient way to run tasks concurrently, on a scheduled basis or asynchronously.
This list is not exhaustive, it covers common terms and concepts used. Visit the following links to learn more about Spring:
- [Spring Boot - Official Website](https://spring.io/projects/spring-boot)
- [Spring Boot - Starter Guide](https://spring.io/quickstart)
Loading…
Cancel
Save